diff options
author | 2022-05-05 21:32:19 -0700 | |
---|---|---|
committer | 2022-05-05 21:32:19 -0700 | |
commit | d629cfafd6fd148c985b5cd051cc5ec48395dc16 (patch) | |
tree | 39a11fb60f61a19802a477a4e690e0f11594c885 /src | |
parent | 7b125c9731815452c62fbfb69b241dc9e4eb7c29 (diff) | |
download | bun-d629cfafd6fd148c985b5cd051cc5ec48395dc16.tar.gz bun-d629cfafd6fd148c985b5cd051cc5ec48395dc16.tar.zst bun-d629cfafd6fd148c985b5cd051cc5ec48395dc16.zip |
E.String gets a Rope
Diffstat (limited to 'src')
-rw-r--r-- | src/bundler.zig | 9 | ||||
-rw-r--r-- | src/bundler/generate_node_modules_bundle.zig | 4 | ||||
-rw-r--r-- | src/bunfig.zig | 10 | ||||
-rw-r--r-- | src/cli/create_command.zig | 52 | ||||
-rw-r--r-- | src/defines.zig | 4 | ||||
-rw-r--r-- | src/env_loader.zig | 12 | ||||
-rw-r--r-- | src/install/install.zig | 12 | ||||
-rw-r--r-- | src/install/npm.zig | 8 | ||||
-rw-r--r-- | src/javascript/jsc/api/transpiler.zig | 2 | ||||
-rw-r--r-- | src/js_ast.zig | 225 | ||||
-rw-r--r-- | src/js_lexer.zig | 10 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 112 | ||||
-rw-r--r-- | src/js_printer.zig | 58 | ||||
-rw-r--r-- | src/json_parser.zig | 25 | ||||
-rw-r--r-- | src/resolver/package_json.zig | 14 | ||||
-rw-r--r-- | src/runtime.zig | 1 | ||||
-rw-r--r-- | src/string_immutable.zig | 1 | ||||
-rw-r--r-- | src/toml/toml_lexer.zig | 2 | ||||
-rw-r--r-- | src/toml/toml_parser.zig | 10 |
19 files changed, 342 insertions, 229 deletions
diff --git a/src/bundler.zig b/src/bundler.zig index 13c83434b..3cfc91690 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -1077,6 +1077,8 @@ pub const Bundler = struct { .ts, .tsx, => { + const platform = bundler.options.platform; + var jsx = this_parse.jsx; jsx.parse = loader.isJSX(); var opts = js_parser.Parser.Options.init(jsx, loader); @@ -1084,6 +1086,7 @@ pub const Bundler = struct { opts.transform_require_to_import = bundler.options.allow_runtime; opts.features.allow_runtime = bundler.options.allow_runtime; opts.features.trim_unused_imports = bundler.options.trim_unused_imports orelse loader.isTypeScript(); + opts.features.should_fold_numeric_constants = platform.isBun(); opts.can_import_from_bundle = bundler.options.node_modules_bundle != null; @@ -1094,7 +1097,7 @@ pub const Bundler = struct { // or you're running in SSR // or the file is a node_module opts.features.hot_module_reloading = bundler.options.hot_module_reloading and - bundler.options.platform.isNotBun() and + platform.isNotBun() and (!opts.can_import_from_bundle or (opts.can_import_from_bundle and !path.isNodeModule())); opts.features.react_fast_refresh = opts.features.hot_module_reloading and @@ -1102,7 +1105,7 @@ pub const Bundler = struct { bundler.options.jsx.supports_fast_refresh; opts.filepath_hash_for_hmr = file_hash orelse 0; opts.features.auto_import_jsx = bundler.options.auto_import_jsx; - opts.warn_about_unbundled_modules = bundler.options.platform.isNotBun(); + opts.warn_about_unbundled_modules = platform.isNotBun(); if (bundler.macro_context == null) { bundler.macro_context = js_ast.Macro.MacroContext.init(bundler); @@ -1114,7 +1117,7 @@ pub const Bundler = struct { opts.macro_context = &bundler.macro_context.?; - opts.features.is_macro_runtime = bundler.options.platform == .bun_macro; + opts.features.is_macro_runtime = platform == .bun_macro; opts.features.replace_exports = this_parse.replace_exports; const value = (bundler.resolver.caches.js.parse( diff --git a/src/bundler/generate_node_modules_bundle.zig b/src/bundler/generate_node_modules_bundle.zig index 44cb2618a..21902f9d9 100644 --- a/src/bundler/generate_node_modules_bundle.zig +++ b/src/bundler/generate_node_modules_bundle.zig @@ -1443,7 +1443,7 @@ pub fn processFile(this: *GenerateNodeModuleBundle, worker: *ThreadPool.Worker, const expr = brk: { // If it's an ascii string, we just print it out with a big old JSON.parse() if (json_parse_result.tag == .ascii) { - json_e_string = js_ast.E.String{ .utf8 = source.contents, .prefer_template = true }; + json_e_string = js_ast.E.String{ .data = source.contents, .prefer_template = true }; var json_string_expr = js_ast.Expr{ .data = .{ .e_string = &json_e_string }, .loc = logger.Loc{ .start = 0 } }; json_call_args[0] = json_string_expr; json_e_identifier = js_ast.E.Identifier{ .ref = Ref.atIndex(json_ast_symbols_list.len - 1) }; @@ -1514,7 +1514,7 @@ pub fn processFile(this: *GenerateNodeModuleBundle, worker: *ThreadPool.Worker, } } - var package_path = js_ast.E.String{ .utf8 = module_data.package_path }; + var package_path = js_ast.E.String{ .data = module_data.package_path }; var target_identifier = E.Identifier{ .ref = register_ref }; var cjs_args: [2]js_ast.G.Arg = undefined; diff --git a/src/bunfig.zig b/src/bunfig.zig index 2592f91db..950c5c8b0 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -49,7 +49,7 @@ pub const Bunfig = struct { switch (expr.data) { .e_string => |str| { - const url = URL.parse(str.utf8); + const url = URL.parse(str.data); // Token if (url.username.len == 0 and url.password.len > 0) { registry.token = url.password; @@ -65,22 +65,22 @@ pub const Bunfig = struct { .e_object => |obj| { if (obj.get("url")) |url| { try this.expect(url, .e_string); - registry.url = url.data.e_string.utf8; + registry.url = url.data.e_string.data; } if (obj.get("username")) |username| { try this.expect(username, .e_string); - registry.username = username.data.e_string.utf8; + registry.username = username.data.e_string.data; } if (obj.get("password")) |password| { try this.expect(password, .e_string); - registry.password = password.data.e_string.utf8; + registry.password = password.data.e_string.data; } if (obj.get("token")) |token| { try this.expect(token, .e_string); - registry.token = token.data.e_string.utf8; + registry.token = token.data.e_string.data; } }, else => { diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index e5d27fe75..6afcdc3c0 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -77,7 +77,7 @@ const UnsupportedPackages = struct { pub fn update(this: *UnsupportedPackages, expr: js_ast.Expr) void { for (expr.data.e_object.properties.slice()) |prop| { inline for (comptime std.meta.fieldNames(UnsupportedPackages)) |field_name| { - if (strings.eqlComptime(prop.key.?.data.e_string.utf8, comptime field_name)) { + if (strings.eqlComptime(prop.key.?.data.e_string.data, comptime field_name)) { @field(this, field_name) = true; } } @@ -747,7 +747,7 @@ pub const CreateCommand = struct { if (package_json_expr.asProperty("name")) |name_expr| { if (name_expr.expr.data == .e_string) { var basename = std.fs.path.basename(destination); - name_expr.expr.data.e_string.utf8 = @intToPtr([*]u8, @ptrToInt(basename.ptr))[0..basename.len]; + name_expr.expr.data.e_string.data = @intToPtr([*]u8, @ptrToInt(basename.ptr))[0..basename.len]; } } @@ -786,7 +786,7 @@ pub const CreateCommand = struct { var i: usize = 0; var out_i: usize = 0; while (i < list.len) : (i += 1) { - const key = list[i].key.?.data.e_string.utf8; + const key = list[i].key.?.data.e_string.data; const do_prune = packages.has(key); prune_count += @intCast(u16, @boolToInt(do_prune)); @@ -844,7 +844,7 @@ pub const CreateCommand = struct { is_nextjs = true; needs.bun_bun_for_nextjs = true; - next_q.expr.data.e_string.utf8 = constStrToU8(target_nextjs_version); + next_q.expr.data.e_string.data = constStrToU8(target_nextjs_version); } has_bun_framework_next = has_bun_framework_next or property.hasAnyPropertyNamed(&.{"bun-framework-next"}); @@ -935,17 +935,17 @@ pub const CreateCommand = struct { const macros_string = "macros"; const bun_macros_relay_path = "bun-macro-relay"; - pub var dependencies_e_string = E.String{ .utf8 = dependencies_string }; - pub var devDependencies_e_string = E.String{ .utf8 = dev_dependencies_string }; - pub var bun_e_string = E.String{ .utf8 = bun_string }; - pub var macros_e_string = E.String{ .utf8 = macros_string }; - pub var react_relay_string = E.String{ .utf8 = "react-relay" }; - pub var bun_macros_relay_path_string = E.String{ .utf8 = "bun-macro-relay" }; - pub var babel_plugin_relay_macro = E.String{ .utf8 = "babel-plugin-relay/macro" }; - pub var babel_plugin_relay_macro_js = E.String{ .utf8 = "babel-plugin-relay/macro.js" }; - pub var graphql_string = E.String{ .utf8 = "graphql" }; + pub var dependencies_e_string = E.String.init(dependencies_string); + pub var devDependencies_e_string = E.String.init(dev_dependencies_string); + pub var bun_e_string = E.String.init(bun_string); + pub var macros_e_string = E.String.init(macros_string); + pub var react_relay_string = E.String.init("react-relay"); + pub var bun_macros_relay_path_string = E.String.init("bun-macro-relay"); + pub var babel_plugin_relay_macro = E.String.init("babel-plugin-relay/macro"); + pub var babel_plugin_relay_macro_js = E.String.init("babel-plugin-relay/macro.js"); + pub var graphql_string = E.String.init("graphql"); - var npx_react_scripts_build_str = E.String{ .utf8 = "npx react-scripts build" }; + var npx_react_scripts_build_str = E.String.init("npx react-scripts build"); pub const npx_react_scripts_build = js_ast.Expr{ .data = .{ .e_string = &npx_react_scripts_build_str }, .loc = logger.Loc.Empty }; @@ -1019,7 +1019,7 @@ pub const CreateCommand = struct { .properties = undefined, }; - var bun_macros_relay_only_object_string = js_ast.E.String{ .utf8 = "macros" }; + var bun_macros_relay_only_object_string = js_ast.E.String.init("macros"); pub var bun_macros_relay_only_object_properties = [_]js_ast.G.Property{ js_ast.G.Property{ .key = js_ast.Expr{ @@ -1038,7 +1038,7 @@ pub const CreateCommand = struct { }; pub var bun_macros_relay_only_object = E.Object{ .properties = undefined }; - var bun_only_macros_string = js_ast.E.String{ .utf8 = "bun" }; + var bun_only_macros_string = js_ast.E.String.init("bun"); pub var bun_only_macros_relay_property = js_ast.G.Property{ .key = js_ast.Expr{ .data = .{ @@ -1054,8 +1054,8 @@ pub const CreateCommand = struct { }, }; - pub var bun_framework_next_string = js_ast.E.String{ .utf8 = "bun-framework-next" }; - pub var bun_framework_next_version = js_ast.E.String{ .utf8 = "latest" }; + pub var bun_framework_next_string = js_ast.E.String.init("bun-framework-next"); + pub var bun_framework_next_version = js_ast.E.String.init("latest"); pub var bun_framework_next_property = js_ast.G.Property{ .key = js_ast.Expr{ .data = .{ @@ -1071,8 +1071,8 @@ pub const CreateCommand = struct { }, }; - pub var bun_macro_relay_dependency_string = js_ast.E.String{ .utf8 = "bun-macro-relay" }; - pub var bun_macro_relay_dependency_version = js_ast.E.String{ .utf8 = "latest" }; + pub var bun_macro_relay_dependency_string = js_ast.E.String.init("bun-macro-relay"); + pub var bun_macro_relay_dependency_version = js_ast.E.String.init("latest"); pub var bun_macro_relay_dependency = js_ast.G.Property{ .key = js_ast.Expr{ @@ -1089,8 +1089,8 @@ pub const CreateCommand = struct { }, }; - pub var refresh_runtime_string = js_ast.E.String{ .utf8 = "react-refresh" }; - pub var refresh_runtime_version = js_ast.E.String{ .utf8 = "0.10.0" }; + pub var refresh_runtime_string = js_ast.E.String.init("react-refresh"); + pub var refresh_runtime_version = js_ast.E.String.init("0.10.0"); pub var react_refresh_dependency = js_ast.G.Property{ .key = js_ast.Expr{ .data = .{ @@ -1350,7 +1350,7 @@ pub const CreateCommand = struct { var script_property_out_i: usize = 0; while (script_property_i < scripts_properties.len) : (script_property_i += 1) { - const script = scripts_properties[script_property_i].value.?.data.e_string.utf8; + const script = scripts_properties[script_property_i].value.?.data.e_string.data; if (strings.contains(script, "react-scripts start") or strings.contains(script, "next dev") or @@ -2079,14 +2079,14 @@ pub const Example = struct { var list = try ctx.allocator.alloc(Example, count); for (q.expr.data.e_object.properties.slice()) |property, i| { - const name = property.key.?.data.e_string.utf8; + const name = property.key.?.data.e_string.data; list[i] = Example{ .name = if (std.mem.indexOfScalar(u8, name, '/')) |slash| name[slash + 1 ..] else name, - .version = property.value.?.asProperty("version").?.expr.data.e_string.utf8, - .description = property.value.?.asProperty("description").?.expr.data.e_string.utf8, + .version = property.value.?.asProperty("version").?.expr.data.e_string.data, + .description = property.value.?.asProperty("description").?.expr.data.e_string.data, }; } return list; diff --git a/src/defines.zig b/src/defines.zig index c50235158..507ad8851 100644 --- a/src/defines.zig +++ b/src/defines.zig @@ -198,8 +198,8 @@ var inf_val = js_ast.E.Number{ .value = std.math.inf_f64 }; const __dirname_str: string = std.fs.path.sep_str ++ "__dirname_is_not_implemented"; const __filename_str: string = "__filename_is_not_implemented.js"; -var __dirname = js_ast.E.String{ .utf8 = __dirname_str }; -var __filename = js_ast.E.String{ .utf8 = __filename_str }; +var __dirname = js_ast.E.String.init(__dirname_str); +var __filename = js_ast.E.String.init(__filename_str); pub const Define = struct { identifiers: std.StringHashMap(IdentifierDefine), diff --git a/src/env_loader.zig b/src/env_loader.zig index 388760513..55e1d74f0 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -517,7 +517,7 @@ pub const Loader = struct { const key_str = std.fmt.allocPrint(key_allocator, "process.env.{s}", .{entry.key_ptr.*}) catch unreachable; e_strings[0] = js_ast.E.String{ - .utf8 = if (value.len > 0) + .data = if (value.len > 0) @intToPtr([*]u8, @ptrToInt(value.ptr))[0..value.len] else &[_]u8{}, @@ -564,7 +564,7 @@ pub const Loader = struct { if (std.mem.indexOfScalar(u64, string_map_hashes, hash)) |key_i| { e_strings[0] = js_ast.E.String{ - .utf8 = if (value.len > 0) + .data = if (value.len > 0) @intToPtr([*]u8, @ptrToInt(value.ptr))[0..value.len] else &[_]u8{}, @@ -614,7 +614,7 @@ pub const Loader = struct { const key = std.fmt.allocPrint(key_allocator, "process.env.{s}", .{entry.key_ptr.*}) catch unreachable; e_strings[0] = js_ast.E.String{ - .utf8 = if (entry.value_ptr.*.len > 0) + .data = if (entry.value_ptr.*.len > 0) @intToPtr([*]u8, @ptrToInt(entry.value_ptr.*.ptr))[0..value.len] else &[_]u8{}, @@ -1167,8 +1167,8 @@ test "DotEnv Loader - copyForDefine" { ); try expect(env_defines.get("process.env.BACON") != null); - try expectString(env_defines.get("process.env.BACON").?.value.e_string.utf8, "false"); - try expectString(env_defines.get("process.env.HOSTNAME").?.value.e_string.utf8, "example.com"); + try expectString(env_defines.get("process.env.BACON").?.value.e_string.data, "false"); + try expectString(env_defines.get("process.env.HOSTNAME").?.value.e_string.data, "example.com"); try expect(env_defines.get("process.env.THIS_SHOULDNT_BE_IN_DEFINES_MAP") != null); user_defines = UserDefine.init(default_allocator); @@ -1176,6 +1176,6 @@ test "DotEnv Loader - copyForDefine" { buf = try loader.copyForDefine(UserDefine, &user_defines, UserDefinesArray, &env_defines, framework, .prefix, "HO", default_allocator); - try expectString(env_defines.get("process.env.HOSTNAME").?.value.e_string.utf8, "example.com"); + try expectString(env_defines.get("process.env.HOSTNAME").?.value.e_string.data, "example.com"); try expect(env_defines.get("process.env.THIS_SHOULDNT_BE_IN_DEFINES_MAP") == null); } diff --git a/src/install/install.zig b/src/install/install.zig index 0e033b960..b16b39813 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -3041,7 +3041,7 @@ pub const PackageManager = struct { new_dependencies[k].key = JSAst.Expr.init( JSAst.E.String, JSAst.E.String{ - .utf8 = update.name, + .data = update.name, }, logger.Loc.Empty, ); @@ -3050,7 +3050,7 @@ pub const PackageManager = struct { JSAst.E.String, JSAst.E.String{ // we set it later - .utf8 = "", + .data = "", }, logger.Loc.Empty, ); @@ -3098,7 +3098,7 @@ pub const PackageManager = struct { .key = JSAst.Expr.init( JSAst.E.String, JSAst.E.String{ - .utf8 = dependency_list, + .data = dependency_list, }, logger.Loc.Empty, ), @@ -3111,7 +3111,7 @@ pub const PackageManager = struct { root_properties[root_properties.len - 1].key = JSAst.Expr.init( JSAst.E.String, JSAst.E.String{ - .utf8 = dependency_list, + .data = dependency_list, }, logger.Loc.Empty, ); @@ -3127,9 +3127,9 @@ pub const PackageManager = struct { var str = update.e_string.?; if (update.version.tag == .uninitialized) { - str.utf8 = latest; + str.data = latest; } else { - str.utf8 = update.version.literal.slice(update.version_buf); + str.data = update.version.literal.slice(update.version_buf); } } } diff --git a/src/install/npm.zig b/src/install/npm.zig index ac12cc942..796a88c7b 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -1031,7 +1031,7 @@ pub const PackageManifest = struct { } }, .e_string => |str| { - package_version.cpu = Architecture.apply(Architecture.none, str.utf8); + package_version.cpu = Architecture.apply(Architecture.none, str.data); }, else => {}, } @@ -1053,7 +1053,7 @@ pub const PackageManifest = struct { } }, .e_string => |str| { - package_version.os = OperatingSystem.apply(OperatingSystem.none, str.utf8); + package_version.os = OperatingSystem.apply(OperatingSystem.none, str.data); }, else => {}, } @@ -1118,11 +1118,11 @@ pub const PackageManifest = struct { break :bin; }, .e_string => |str| { - if (str.utf8.len > 0) { + if (str.data.len > 0) { package_version.bin = Bin{ .tag = Bin.Tag.file, .value = .{ - .file = string_builder.append(String, str.utf8), + .file = string_builder.append(String, str.data), }, }; break :bin; diff --git a/src/javascript/jsc/api/transpiler.zig b/src/javascript/jsc/api/transpiler.zig index 9b4fce4de..404475460 100644 --- a/src/javascript/jsc/api/transpiler.zig +++ b/src/javascript/jsc/api/transpiler.zig @@ -317,7 +317,7 @@ fn exportReplacementValue(value: JSValue, globalThis: *JSGlobalObject) ?JSAst.Ex if (value.isString()) { var str = JSAst.E.String{ - .utf8 = std.fmt.allocPrint(bun.default_allocator, "{}", .{value.getZigString(globalThis)}) catch unreachable, + .data = std.fmt.allocPrint(bun.default_allocator, "{}", .{value.getZigString(globalThis)}) catch unreachable, }; var out = bun.default_allocator.create(JSAst.E.String) catch unreachable; out.* = str; diff --git a/src/js_ast.zig b/src/js_ast.zig index 80f754a2c..96b14c363 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -1328,6 +1328,11 @@ pub const E = struct { return @floatToInt(u64, @maximum(@trunc(self.value), 0)); } + pub inline fn toUsize(self: Number) usize { + @setRuntimeSafety(false); + return @floatToInt(usize, @maximum(@trunc(self.value), 0)); + } + pub inline fn toU32(self: Number) u32 { @setRuntimeSafety(false); return @floatToInt(u32, @maximum(@trunc(self.value), 0)); @@ -1421,7 +1426,7 @@ pub const E = struct { pub const SetError = error{ OutOfMemory, Clobber }; pub fn set(self: *const Object, key: Expr, allocator: std.mem.Allocator, value: Expr) SetError!void { - if (self.hasProperty(key.data.e_string.utf8)) return error.Clobber; + if (self.hasProperty(key.data.e_string.data)) return error.Clobber; try self.properties.push(allocator, .{ .key = key, .value = value, @@ -1435,7 +1440,7 @@ pub const E = struct { // this is terribly, shamefully slow pub fn setRope(self: *Object, rope: *const Rope, allocator: std.mem.Allocator, value: Expr) SetError!void { - if (self.get(rope.head.data.e_string.utf8)) |existing| { + if (self.get(rope.head.data.e_string.data)) |existing| { switch (existing.data) { .e_array => |array| { if (rope.next == null) { @@ -1483,7 +1488,7 @@ pub const E = struct { } pub fn getOrPutObject(self: *Object, rope: *const Rope, allocator: std.mem.Allocator) SetError!Expr { - if (self.get(rope.head.data.e_string.utf8)) |existing| { + if (self.get(rope.head.data.e_string.data)) |existing| { switch (existing.data) { .e_array => |array| { if (rope.next == null) { @@ -1533,7 +1538,7 @@ pub const E = struct { } pub fn getOrPutArray(self: *Object, rope: *const Rope, allocator: std.mem.Allocator) SetError!Expr { - if (self.get(rope.head.data.e_string.utf8)) |existing| { + if (self.get(rope.head.data.e_string.data)) |existing| { switch (existing.data) { .e_array => |array| { if (rope.next == null) { @@ -1653,17 +1658,17 @@ pub const E = struct { var rhs_key_size: u8 = @enumToInt(Fields.__fake); if (lhs.key != null and lhs.key.?.data == .e_string) { - lhs_key_size = @enumToInt(Map.get(lhs.key.?.data.e_string.utf8) orelse Fields.__fake); + lhs_key_size = @enumToInt(Map.get(lhs.key.?.data.e_string.data) orelse Fields.__fake); } if (rhs.key != null and rhs.key.?.data == .e_string) { - rhs_key_size = @enumToInt(Map.get(rhs.key.?.data.e_string.utf8) orelse Fields.__fake); + rhs_key_size = @enumToInt(Map.get(rhs.key.?.data.e_string.data) orelse Fields.__fake); } return switch (std.math.order(lhs_key_size, rhs_key_size)) { .lt => true, .gt => false, - .eq => strings.cmpStringsAsc(ctx, lhs.key.?.data.e_string.utf8, rhs.key.?.data.e_string.utf8), + .eq => strings.cmpStringsAsc(ctx, lhs.key.?.data.e_string.data, rhs.key.?.data.e_string.data), }; } }; @@ -1671,7 +1676,7 @@ pub const E = struct { const Sorter = struct { pub fn isLessThan(ctx: void, lhs: G.Property, rhs: G.Property) bool { - return strings.cmpStringsAsc(ctx, lhs.key.?.data.e_string.utf8, rhs.key.?.data.e_string.utf8); + return strings.cmpStringsAsc(ctx, lhs.key.?.data.e_string.data, rhs.key.?.data.e_string.data); } }; }; @@ -1682,36 +1687,110 @@ pub const E = struct { pub const String = struct { // A version of this where `utf8` and `value` are stored in a packed union, with len as a single u32 was attempted. // It did not improve benchmarks. Neither did converting this from a heap-allocated type to a stack-allocated type. - value: []const u16 = &.{}, - utf8: bun.string = &([_]u8{}), + data: []const u8 = "", prefer_template: bool = false, - pub var empty = String{}; - pub var @"true" = String{ .utf8 = "true" }; - pub var @"false" = String{ .utf8 = "false" }; - pub var @"null" = String{ .utf8 = "null" }; - pub var @"undefined" = String{ .utf8 = "undefined" }; + // A very simple rope implementation + // We only use this for string folding, so this is kind of overkill + // We don't need to deal with substrings + next: ?*String = null, + end: ?*String = null, + rope_len: u32 = 0, + is_utf16: bool = false, - pub fn clone(str: *const String, allocator: std.mem.Allocator) !String { - if (str.isUTF8()) { - return String{ - .utf8 = try allocator.dupe(u8, str.utf8), - .prefer_template = str.prefer_template, - }; + pub fn push(this: *String, other: *String) void { + std.debug.assert(this.isUTF8()); + std.debug.assert(other.isUTF8()); + + if (other.rope_len == 0) { + other.rope_len = @truncate(u32, other.data.len); + } + + if (this.rope_len == 0) { + this.rope_len = @truncate(u32, this.data.len); + } + + this.rope_len += other.rope_len; + if (this.next == null) { + this.next = other; + this.end = other; } else { - return String{ - .value = try allocator.dupe(u16, str.value), - .prefer_template = str.prefer_template, + this.end.?.next = other; + this.end = other; + } + } + + pub fn toUTF8(this: *String, allocator: std.mem.Allocator) !void { + if (!this.is_utf16) return; + this.data = try strings.toUTF8Alloc(allocator, this.slice16()); + this.is_utf16 = false; + } + + pub fn init(value: anytype) String { + const Value = @TypeOf(value); + if (Value == []u16 or Value == []const u16) { + return .{ + .data = @ptrCast([*]const u8, value.ptr)[0..value.len], + .is_utf16 = true, }; } + return .{ + .data = value, + }; + } + + pub fn slice16(this: *const String) []const u16 { + std.debug.assert(this.is_utf16); + return @ptrCast([*]const u16, @alignCast(@alignOf(u16), this.data.ptr))[0..this.data.len]; + } + + pub fn resovleRopeIfNeeded(this: *String, allocator: std.mem.Allocator) void { + if (this.next == null or !this.isUTF8()) return; + var bytes = allocator.alloc(u8, this.rope_len) catch unreachable; + var ptr = bytes.ptr; + var remain = bytes.len; + @memcpy(ptr, this.data.ptr, this.data.len); + ptr += this.data.len; + remain -= this.data.len; + var str = this.next; + while (str) |strin| { + @memcpy(ptr, strin.data.ptr, strin.data.len); + ptr += strin.data.len; + remain -= strin.data.len; + var prev = strin; + str = strin.next; + prev.next = null; + prev.end = null; + } + this.data = bytes; + this.next = null; + } + + pub fn slice(this: *String, allocator: std.mem.Allocator) []const u8 { + this.resovleRopeIfNeeded(allocator); + return @ptrCast([*]const u8, @alignCast(@alignOf(u8), this.data.ptr))[0..this.data.len]; + } + + pub var empty = String{}; + pub var @"true" = String{ .data = "true" }; + pub var @"false" = String{ .data = "false" }; + pub var @"null" = String{ .data = "null" }; + pub var @"undefined" = String{ .data = "undefined" }; + + pub fn clone(str: *const String, allocator: std.mem.Allocator) !String { + return String{ + .data = try allocator.dupe(u8, str.data), + .prefer_template = str.prefer_template, + .is_utf16 = !str.isUTF8(), + }; } pub inline fn len(s: *const String) usize { - return @maximum(s.utf8.len, s.value.len); + return if (s.rope_len > 0) s.rope_len else s.data.len; } pub inline fn isUTF8(s: *const String) bool { - return s.len() == s.utf8.len; + return !s.is_utf16; } pub inline fn isBlank(s: *const String) bool { @@ -1727,16 +1806,16 @@ pub const E = struct { switch (_t) { @This() => { if (other.isUTF8()) { - return strings.eql(s.utf8, other.utf8); + return strings.eql(s.data, other.data); } else { - return strings.utf16EqlString(other.value, s.utf8); + return strings.utf16EqlString(other.slice16(), s.data); } }, bun.string => { - return strings.eql(s.utf8, other); + return strings.eql(s.data, other); }, []u16, []const u16 => { - return strings.utf16EqlString(other, s.utf8); + return strings.utf16EqlString(other, s.data); }, else => { @compileError("Invalid type"); @@ -1746,16 +1825,16 @@ pub const E = struct { switch (_t) { @This() => { if (other.isUTF8()) { - return strings.utf16EqlString(s.value, other.utf8); + return strings.utf16EqlString(s.slice16(), other.data); } else { - return std.mem.eql(u16, other.value, s.value); + return std.mem.eql(u16, other.slice16(), s.slice16()); } }, bun.string => { - return strings.utf16EqlString(s.value, other); + return strings.utf16EqlString(s.slice16(), other); }, []u16, []const u16 => { - return std.mem.eql(u16, other.value, s.value); + return std.mem.eql(u16, other.slice16(), s.slice16()); }, else => { @compileError("Invalid type"); @@ -1766,16 +1845,16 @@ pub const E = struct { pub fn eqlComptime(s: *const String, comptime value: anytype) bool { return if (s.isUTF8()) - strings.eqlComptime(s.utf8, value) + strings.eqlComptime(s.data, value) else - strings.eqlComptimeUTF16(s.value, value); + strings.eqlComptimeUTF16(s.slice16(), value); } pub fn string(s: *const String, allocator: std.mem.Allocator) !bun.string { if (s.isUTF8()) { - return s.utf8; + return s.data; } else { - return strings.toUTF8Alloc(allocator, s.value); + return strings.toUTF8Alloc(allocator, s.slice16()); } } @@ -1784,17 +1863,17 @@ pub const E = struct { if (s.isUTF8()) { // hash utf-8 - return std.hash.Wyhash.hash(0, s.utf8); + return std.hash.Wyhash.hash(0, s.data); } else { // hash utf-16 - return std.hash.Wyhash.hash(0, @ptrCast([*]const u8, s.value.ptr)[0 .. s.value.len * 2]); + return std.hash.Wyhash.hash(0, @ptrCast([*]const u8, s.slice16().ptr)[0 .. s.slice16().len * 2]); } } pub fn jsonStringify(s: *const String, options: anytype, writer: anytype) !void { var buf = [_]u8{0} ** 4096; var i: usize = 0; - for (s.value) |char| { + for (s.slice16()) |char| { buf[i] = @intCast(u8, char); i += 1; if (i >= 4096) { @@ -2233,13 +2312,13 @@ pub const Expr = struct { if (list.len > 0) { list = list[1 .. list.len - 1]; } - return Expr.init(E.String, E.String{ .utf8 = list }, loc); + return Expr.init(E.String, E.String.init(list), loc); } return Expr.init( E.String, E.String{ - .utf8 = try JSC.ZigString.init(bytes).toBase64DataURL(allocator), + .data = try JSC.ZigString.init(bytes).toBase64DataURL(allocator), }, loc, ); @@ -2272,7 +2351,7 @@ pub const Expr = struct { const key = prop.key orelse continue; if (std.meta.activeTag(key.data) != .e_string) continue; const key_str = key.data.e_string; - if (strings.eqlAnyComptime(key_str.utf8, names)) return true; + if (strings.eqlAnyComptime(key_str.data, names)) return true; } return false; @@ -2287,7 +2366,7 @@ pub const Expr = struct { } pub fn getRope(self: *const Expr, rope: *const E.Object.Rope) ?E.Object.RopeQuery { - if (self.get(rope.head.data.e_string.utf8)) |existing| { + if (self.get(rope.head.data.e_string.data)) |existing| { switch (existing.data) { .e_array => |array| { if (rope.next) |next| { @@ -2358,7 +2437,7 @@ pub const Expr = struct { const key_str = expr.data.e_string; - return if (key_str.isUTF8()) key_str.utf8 else key_str.string(allocator) catch null; + return if (key_str.isUTF8()) key_str.data else key_str.string(allocator) catch null; } pub fn asBool( @@ -2750,11 +2829,9 @@ pub const Expr = struct { E.String => { if (comptime Environment.isDebug) { // Sanity check: assert string is not a null ptr - if (st.isUTF8() and st.utf8.len > 0) { - std.debug.assert(@ptrToInt(st.utf8.ptr) > 0); - std.debug.assert(st.utf8[0] > 0); - } else if (st.value.len > 0) { - std.debug.assert(@ptrToInt(st.value.ptr) > 0); + if (st.data.len > 0 and st.isUTF8()) { + std.debug.assert(@ptrToInt(st.data.ptr) > 0); + std.debug.assert(st.data[0] > 0); } } return Expr{ @@ -5051,9 +5128,9 @@ pub const Macro = struct { } if (str.isUTF8()) { - return JSC.ZigString.init(str.utf8).toValue(ctx.ptr()).asRef(); + return JSC.ZigString.init(str.data).toValue(ctx.ptr()).asRef(); } else { - return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.value.ptr, str.value.len)); + return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.slice16().ptr, str.slice16().len)); } } @@ -5198,9 +5275,9 @@ pub const Macro = struct { } if (str.isUTF8()) { - return JSC.ZigString.init(str.utf8).toValue(ctx.ptr()).asRef(); + return JSC.ZigString.init(str.data).toValue(ctx.ptr()).asRef(); } else { - return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.value.ptr, str.value.len)); + return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.slice16().ptr, str.slice16().len)); } }, // .e_number => |number| { @@ -5946,7 +6023,7 @@ pub const Macro = struct { for (props) |prop, i| { const key = prop.key orelse continue; if (key.data != .e_string or !key.data.e_string.isUTF8()) continue; - if (strings.eqlComptime(key.data.e_string.utf8, name)) return @intCast(u32, i); + if (strings.eqlComptime(key.data.e_string.data, name)) return @intCast(u32, i); } return null; @@ -5956,7 +6033,7 @@ pub const Macro = struct { for (props) |prop| { const key = prop.key orelse continue; if (key.data != .e_string or !key.data.e_string.isUTF8()) continue; - if (strings.eqlComptime(key.data.e_string.utf8, name)) return prop.value; + if (strings.eqlComptime(key.data.e_string.data, name)) return prop.value; } return null; @@ -6086,7 +6163,7 @@ pub const Macro = struct { return self.writeElement(el.*); }, .e_string => |str| { - self.args.appendAssumeCapacity(Expr.init(E.BigInt, E.BigInt{ .value = std.mem.trimRight(u8, str.utf8, "n") }, value.loc)); + self.args.appendAssumeCapacity(Expr.init(E.BigInt, E.BigInt{ .value = std.mem.trimRight(u8, str.data, "n") }, value.loc)); }, .e_big_int => { self.args.appendAssumeCapacity(value); @@ -6316,7 +6393,7 @@ pub const Macro = struct { switch (value.data) { .e_string => |str| { - self.args.appendAssumeCapacity(Expr.init(E.RegExp, E.RegExp{ .value = str.utf8 }, value.loc)); + self.args.appendAssumeCapacity(Expr.init(E.RegExp, E.RegExp{ .value = str.data }, value.loc)); }, .e_reg_exp => { self.args.appendAssumeCapacity(value); @@ -6687,17 +6764,17 @@ pub const Macro = struct { const str = tag_expr.data.e_string; var p = self.p; - const node_type: JSNode.Tag = JSNode.Tag.names.get(str.utf8) orelse { + const node_type: JSNode.Tag = JSNode.Tag.names.get(str.data) orelse { if (!str.isUTF8()) { - self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{strings.toUTF8Alloc(self.p.allocator, str.value)}) catch unreachable; + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{strings.toUTF8Alloc(self.p.allocator, str.slice16())}) catch unreachable; } else { - self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{str.utf8}) catch unreachable; + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{str.data}) catch unreachable; } return false; }; if (!valid_tags.get(node_type)) { - self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid here", .{str.utf8}) catch unreachable; + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid here", .{str.data}) catch unreachable; } return self.writeNodeType(node_type, element.properties.slice(), element.children.slice(), tag_expr.loc); @@ -6709,11 +6786,11 @@ pub const Macro = struct { const str = tag_expr.data.e_string; var p = self.p; - const node_type: JSNode.Tag = JSNode.Tag.names.get(str.utf8) orelse { + const node_type: JSNode.Tag = JSNode.Tag.names.get(str.data) orelse { if (!str.isUTF8()) { - self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{strings.toUTF8Alloc(self.p.allocator, str.value)}) catch unreachable; + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{strings.toUTF8Alloc(self.p.allocator, str.slice16())}) catch unreachable; } else { - self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{str.utf8}) catch unreachable; + self.log.addErrorFmt(p.source, tag_expr.loc, p.allocator, "Tag \"{s}\" is invalid", .{str.data}) catch unreachable; } return false; }; @@ -7142,9 +7219,9 @@ pub const Macro = struct { }, }; } else if (wtf_string.is8Bit()) { - expr.* = Expr.init(E.String, E.String{ .utf8 = wtf_string.characters8()[0..wtf_string.length()] }, writer.loc); + expr.* = Expr.init(E.String, E.String.init(wtf_string.characters8()[0..wtf_string.length()]), writer.loc); } else if (wtf_string.is16Bit()) { - expr.* = Expr.init(E.String, E.String{ .value = wtf_string.characters16()[0..wtf_string.length()] }, writer.loc); + expr.* = Expr.init(E.String, E.String.init(wtf_string.characters16()[0..wtf_string.length()]), writer.loc); } else { unreachable; } @@ -7481,7 +7558,7 @@ pub const Macro = struct { for (properties) |property| { const key = property.key orelse continue; if (key.data != .e_string) continue; - const str = key.data.e_string.utf8; + const str = key.data.e_string.data; if (strings.eql(property_slice, str)) { const value = property.value orelse return js.JSValueMakeUndefined(ctx); @@ -7508,7 +7585,7 @@ pub const Macro = struct { for (properties) |property| { const key = property.key orelse continue; if (key.data != .e_string) continue; - const str = key.data.e_string.utf8; + const str = key.data.e_string.data; if (strings.eql(property_slice, str)) return true; } @@ -7528,7 +7605,7 @@ pub const Macro = struct { for (properties) |property| { const key = property.key orelse continue; if (key.data != .e_string) continue; - const str = key.data.e_string.utf8; + const str = key.data.e_string.data; js.JSPropertyNameAccumulatorAddName(props, js.JSStringCreateStatic(str.ptr, str.len)); } } @@ -7926,10 +8003,10 @@ pub const Macro = struct { var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(array, i); defer JSC.C.JSStringRelease(property_name_ref); properties[i] = G.Property{ - .key = Expr.init(E.String, E.String{ .utf8 = this.allocator.dupe( + .key = Expr.init(E.String, E.String.init(this.allocator.dupe( u8, JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..JSC.C.JSStringGetLength(property_name_ref)], - ) catch unreachable }, this.caller.loc), + ) catch unreachable), this.caller.loc), .value = try this.run( JSC.JSValue.fromRef(JSC.C.JSObjectGetProperty(this.global.ref(), object, property_name_ref, null)), ), @@ -7966,7 +8043,7 @@ pub const Macro = struct { var zig_str = value.getZigString(this.global); zig_str.detectEncoding(); var sliced = zig_str.toSlice(this.allocator); - return Expr.init(E.String, E.String{ .utf8 = sliced.slice() }, this.caller.loc); + return Expr.init(E.String, E.String.init(sliced.slice()), this.caller.loc); }, .Promise => { var _entry = this.visited.getOrPut(this.allocator, value) catch unreachable; diff --git a/src/js_lexer.zig b/src/js_lexer.zig index 6e8563c70..d771b919a 100644 --- a/src/js_lexer.zig +++ b/src/js_lexer.zig @@ -1853,17 +1853,19 @@ fn NewLexer_( pub fn toEString(lexer: *LexerType) js_ast.E.String { if (lexer.string_literal_is_ascii) { - return js_ast.E.String{ .utf8 = lexer.string_literal_slice }; + return js_ast.E.String.init(lexer.string_literal_slice); } else { - return js_ast.E.String{ .value = lexer.allocator.dupe(u16, lexer.string_literal) catch unreachable }; + return js_ast.E.String.init(lexer.allocator.dupe(u16, lexer.string_literal) catch unreachable); } } pub fn toUTF8EString(lexer: *LexerType) js_ast.E.String { if (lexer.string_literal_is_ascii) { - return js_ast.E.String{ .utf8 = lexer.string_literal_slice }; + return js_ast.E.String.init(lexer.string_literal_slice); } else { - return js_ast.E.String{ .utf8 = lexer.utf16ToString(lexer.string_literal) }; + var e_str = js_ast.E.String.init(lexer.string_literal); + e_str.toUTF8(lexer.allocator) catch unreachable; + return e_str; } } diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index f36d51981..291466dd0 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -103,6 +103,29 @@ pub const AllocatedNamesPool = ObjectPool( 4, ); +fn foldStringAddition(lhs: Expr, rhs: Expr) ?Expr { + switch (lhs.data) { + .e_string => |left| { + if (rhs.data == .e_string and left.isUTF8() and rhs.data.e_string.isUTF8()) { + lhs.data.e_string.push(rhs.data.e_string); + return lhs; + } + }, + .e_binary => |bin| { + + // 123 + "bar" + "baz" + if (bin.op == .bin_add) { + if (foldStringAddition(bin.right, rhs)) |out| { + return Expr.init(E.Binary, E.Binary{ .op = bin.op, .left = bin.left, .right = out }, lhs.loc); + } + } + }, + else => {}, + } + + return null; +} + // If we are currently in a hoisted child of the module scope, relocate these // declarations to the top level and return an equivalent assignment statement. // Make sure to check that the declaration kind is "var" before calling this. @@ -222,7 +245,7 @@ const JSXTag = struct { if (strings.containsComptime(name, "-:") or (p.lexer.token != .t_dot and name[0] >= 'a' and name[0] <= 'z')) { return JSXTag{ .data = Data{ .tag = p.e(E.String{ - .utf8 = name, + .data = name, }, loc) }, .range = tag_range, }; @@ -2287,6 +2310,7 @@ pub const Parser = struct { fn _parse(self: *Parser, comptime ParserType: type) !js_ast.Result { var p: ParserType = undefined; try ParserType.init(self.allocator, self.log, self.source, self.define, self.lexer, self.options, &p); + p.should_fold_numeric_constants = self.options.features.should_fold_numeric_constants; defer p.lexer.deinit(); var result: js_ast.Result = undefined; @@ -2577,7 +2601,7 @@ pub const Parser = struct { }, loc, ), - .value = p.e(E.String{ .utf8 = p.source.path.pretty }, loc), + .value = p.e(E.String{ .data = p.source.path.pretty }, loc), }; decl_i += 1; } @@ -3032,7 +3056,7 @@ pub const Prefill = struct { Expr{ .data = .{ .e_boolean = E.Boolean{ .value = false } }, .loc = logger.Loc.Empty }, }; pub var ActivateString = E.String{ - .utf8 = "activate", + .data = "activate", }; pub var ActivateIndex = E.Index{ .index = .{ @@ -3056,11 +3080,11 @@ pub const Prefill = struct { pub const Zero = E.Number{ .value = 0.0 }; }; pub const String = struct { - pub var Key = E.String{ .utf8 = &Prefill.StringLiteral.Key }; - pub var Children = E.String{ .utf8 = &Prefill.StringLiteral.Children }; - pub var Filename = E.String{ .utf8 = &Prefill.StringLiteral.Filename }; - pub var LineNumber = E.String{ .utf8 = &Prefill.StringLiteral.LineNumber }; - pub var ColumnNumber = E.String{ .utf8 = &Prefill.StringLiteral.ColumnNumber }; + pub var Key = E.String{ .data = &Prefill.StringLiteral.Key }; + pub var Children = E.String{ .data = &Prefill.StringLiteral.Children }; + pub var Filename = E.String{ .data = &Prefill.StringLiteral.Filename }; + pub var LineNumber = E.String{ .data = &Prefill.StringLiteral.LineNumber }; + pub var ColumnNumber = E.String{ .data = &Prefill.StringLiteral.ColumnNumber }; }; pub const Data = struct { pub var BMissing = B{ .b_missing = BMissing_ }; @@ -7883,7 +7907,7 @@ fn NewParser_( try p.lexer.next(); - key = p.e(E.String{ .utf8 = name }, loc); + key = p.e(E.String{ .data = name }, loc); if (p.lexer.token != .t_colon and p.lexer.token != .t_open_paren) { const ref = p.storeNameInRef(name) catch unreachable; @@ -7997,7 +8021,7 @@ fn NewParser_( if (p.lexer.token == .t_string_literal) { value.name = p.lexer.toEString(); } else if (p.lexer.isIdentifierOrKeyword()) { - value.name = E.String{ .utf8 = p.lexer.identifier }; + value.name = E.String{ .data = p.lexer.identifier }; needs_symbol = true; } else { try p.lexer.expect(.t_identifier); @@ -9276,7 +9300,7 @@ fn NewParser_( } } - key = p.e(E.String{ .utf8 = name }, name_range.loc); + key = p.e(E.String{ .data = name }, name_range.loc); // Parse a shorthand property const isShorthandProperty = !opts.is_class and @@ -11307,7 +11331,7 @@ fn NewParser_( if (comptime only_scan_imports_and_do_not_visit) { if (value.data == .e_string and value.data.e_string.isUTF8() and value.data.e_string.isPresent()) { - const import_record_index = p.addImportRecord(.dynamic, value.loc, value.data.e_string.utf8); + const import_record_index = p.addImportRecord(.dynamic, value.loc, value.data.e_string.slice(p.allocator)); return p.e(E.Import{ .expr = value, @@ -11390,7 +11414,7 @@ fn NewParser_( continue; } - const prop_name = p.e(E.String{ .utf8 = prop_name_literal }, key_range.loc); + const prop_name = p.e(E.String{ .data = prop_name_literal }, key_range.loc); // Parse the value var value: Expr = undefined; @@ -11430,13 +11454,13 @@ fn NewParser_( const key = brk: { switch (expr.data) { .e_import_identifier => |ident| { - break :brk p.e(E.String{ .utf8 = p.loadNameFromRef(ident.ref) }, expr.loc); + break :brk p.e(E.String{ .data = p.loadNameFromRef(ident.ref) }, expr.loc); }, .e_identifier => |ident| { - break :brk p.e(E.String{ .utf8 = p.loadNameFromRef(ident.ref) }, expr.loc); + break :brk p.e(E.String{ .data = p.loadNameFromRef(ident.ref) }, expr.loc); }, .e_dot => |dot| { - break :brk p.e(E.String{ .utf8 = dot.name }, dot.name_loc); + break :brk p.e(E.String{ .data = dot.name }, dot.name_loc); }, .e_index => |index| { if (index.index.data == .e_string) { @@ -12088,14 +12112,14 @@ fn NewParser_( const runtime = if (p.options.jsx.runtime == .automatic and !e_.flags.is_key_before_rest) options.JSX.Runtime.automatic else options.JSX.Runtime.classic; var children_count = e_.children.len; - const is_childless_tag = FeatureFlags.react_specific_warnings and children_count > 0 and tag.data == .e_string and tag.data.e_string.isUTF8() and js_lexer.ChildlessJSXTags.has(tag.data.e_string.utf8); + const is_childless_tag = FeatureFlags.react_specific_warnings and children_count > 0 and tag.data == .e_string and tag.data.e_string.isUTF8() and js_lexer.ChildlessJSXTags.has(tag.data.e_string.slice(p.allocator)); children_count = if (is_childless_tag) 0 else children_count; if (children_count != e_.children.len) { // Error: meta is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`. // ^ from react-dom - p.log.addWarningFmt(p.source, tag.loc, p.allocator, "<{s} /> is a void element and must not have \"children\"", .{tag.data.e_string.utf8}) catch {}; + p.log.addWarningFmt(p.source, tag.loc, p.allocator, "<{s} /> is a void element and must not have \"children\"", .{tag.data.e_string.slice(p.allocator)}) catch {}; } // TODO: maybe we should split these into two different AST Nodes @@ -12395,7 +12419,7 @@ fn NewParser_( const jsx_element = e_.right.data.e_jsx_element; if (jsx_element.tag) |tag| { if (tag.data == .e_string) { - const tag_string = tag.data.e_string.utf8; + const tag_string = tag.data.e_string.slice(p.allocator); if (js_ast.Macro.JSNode.Tag.names.get(tag_string)) |node_tag| { call_args[1] = Expr{ .loc = tag.loc, .data = js_ast.Macro.JSNode.Tag.ids.get(node_tag) }; } else { @@ -12420,7 +12444,7 @@ fn NewParser_( const jsx_element = e_.left.data.e_jsx_element; if (jsx_element.tag) |tag| { if (tag.data == .e_string) { - const tag_string = tag.data.e_string.utf8; + const tag_string = tag.data.e_string.slice(p.allocator); if (js_ast.Macro.JSNode.Tag.names.get(tag_string)) |node_tag| { call_args[0] = Expr{ .loc = tag.loc, .data = js_ast.Macro.JSNode.Tag.ids.get(node_tag) }; } else { @@ -12632,7 +12656,9 @@ fn NewParser_( } } - // TODO: fold string addition + if (foldStringAddition(e_.left, e_.right)) |res| { + return res; + } }, .bin_sub => { if (p.should_fold_numeric_constants) { @@ -12822,11 +12848,9 @@ fn NewParser_( } if (e_.optional_chain == null and e_.index.data == .e_string and e_.index.data.e_string.isUTF8()) { - const literal = e_.index.data.e_string.utf8; + const literal = e_.index.data.e_string.slice(p.allocator); if (p.maybeRewritePropertyAccess( expr.loc, - in.assign_target, - is_delete_target, e_.target, literal, e_.index.loc, @@ -12857,8 +12881,14 @@ fn NewParser_( } } } + // "foo"[2] + } else if (e_.optional_chain == null and target.data == .e_string and e_.index.data == .e_number and target.data.e_string.isUTF8() and e_.index.data.e_number.value >= 0) { + const literal = target.data.e_string.slice(p.allocator); + const index = e_.index.data.e_number.toUsize(); + if (literal.len > index) { + return p.e(E.String{ .data = literal[index .. index + 1] }, expr.loc); + } } - // Create an error for assigning to an import namespace when bundling. Even // though this is a run-time error, we make it a compile-time error when // bundling because scope hoisting means these will no longer be run-time @@ -12894,7 +12924,7 @@ fn NewParser_( } if (SideEffects.typeof(e_.value.data)) |typeof| { - return p.e(E.String{ .utf8 = typeof }, expr.loc); + return p.e(E.String{ .data = typeof }, expr.loc); } }, .un_delete => { @@ -13023,8 +13053,6 @@ fn NewParser_( if (e_.optional_chain == null) { if (p.maybeRewritePropertyAccess( expr.loc, - in.assign_target, - is_delete_target, e_.target, e_.name, e_.name_loc, @@ -13034,11 +13062,9 @@ fn NewParser_( } if (comptime allow_macros) { - if (p.macro_call_count > 0) { - if (e_.target.data == .e_object and e_.target.data.e_object.was_originally_macro) { - if (e_.target.get(e_.name)) |obj| { - return obj; - } + if (p.macro_call_count > 0 and e_.target.data == .e_object and e_.target.data.e_object.was_originally_macro) { + if (e_.target.get(e_.name)) |obj| { + return obj; } } } @@ -13166,7 +13192,7 @@ fn NewParser_( key.data.isStringValue() and strings.eqlComptime( // __proto__ is utf8, assume it lives in refs - key.data.e_string.utf8, + key.data.e_string.slice(p.allocator), "__proto__", )) { if (has_proto) { @@ -13869,8 +13895,6 @@ fn NewParser_( fn maybeRewritePropertyAccess( p: *P, loc: logger.Loc, - _: js_ast.AssignTarget, - _: bool, target: js_ast.Expr, name: string, name_loc: logger.Loc, @@ -13896,6 +13920,12 @@ fn NewParser_( } } }, + .e_string => |str| { + // minify "long-string".length to 11 + if (strings.eqlComptime(name, "length")) { + return p.e(E.Number{ .value = @intToFloat(f64, str.len()) }, loc); + } + }, else => {}, } @@ -15513,7 +15543,7 @@ fn NewParser_( } const last = parts.len - 1; - const is_tail_match = strings.eql(parts[last], index.index.data.e_string.utf8); + const is_tail_match = strings.eql(parts[last], index.index.data.e_string.slice(p.allocator)); return is_tail_match and p.isDotDefineMatch(index.target, parts[0..last]); } }, @@ -15906,7 +15936,7 @@ fn NewParser_( p.expr_list.appendAssumeCapacity(p.e(E.Identifier{ .ref = ref, }, loc)); - p.expr_list.appendAssumeCapacity(p.e(E.String{ .utf8 = name }, loc)); + p.expr_list.appendAssumeCapacity(p.e(E.String{ .data = name }, loc)); return p.s(S.SExpr{ // I believe that this is a spot we can do $RefreshReg$(name) .value = p.callRuntime(loc, "__name", p.expr_list.items[start..p.expr_list.items.len]), @@ -16431,7 +16461,7 @@ fn NewParser_( sourcefile_name = sourcefile_name[end..]; } } - commonjs_wrapper.data.e_call.args.ptr[1] = p.e(E.String{ .utf8 = sourcefile_name }, logger.Loc.Empty); + commonjs_wrapper.data.e_call.args.ptr[1] = p.e(E.String{ .data = sourcefile_name }, logger.Loc.Empty); new_stmts_list[imports_list.len] = p.s( S.ExportDefault{ @@ -16558,7 +16588,7 @@ fn NewParser_( new_call_args[0] = p.e(E.Number{ .value = @intToFloat(f64, p.options.filepath_hash_for_hmr) }, logger.Loc.Empty); // This helps us provide better error messages - new_call_args[1] = p.e(E.String{ .utf8 = p.source.path.pretty }, logger.Loc.Empty); + new_call_args[1] = p.e(E.String{ .data = p.source.path.pretty }, logger.Loc.Empty); if (p.options.features.react_fast_refresh) { new_call_args[2] = p.e(E.Identifier{ .ref = p.jsx_refresh_runtime.ref }, logger.Loc.Empty); } @@ -16685,7 +16715,7 @@ fn NewParser_( ); export_properties[named_export_i] = G.Property{ - .key = p.e(E.String{ .utf8 = named_export.key_ptr.* }, logger.Loc.Empty), + .key = p.e(E.String{ .data = named_export.key_ptr.* }, logger.Loc.Empty), .value = p.e( E.Arrow{ .args = &[_]G.Arg{}, diff --git a/src/js_printer.zig b/src/js_printer.zig index 89bb82a1e..8bdaae5fc 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -926,9 +926,9 @@ pub fn NewPrinter( pub fn bestQuoteCharForEString(p: *Printer, str: *const E.String, allow_backtick: bool) u8 { if (str.isUTF8()) { - return p.bestQuoteCharForString(str.utf8, allow_backtick); + return p.bestQuoteCharForString(str.data, allow_backtick); } else { - return p.bestQuoteCharForString(str.value, allow_backtick); + return p.bestQuoteCharForString(str.slice16(), allow_backtick); } } @@ -1928,6 +1928,7 @@ pub fn NewPrinter( p.print(if (e.value) "true" else "false"); }, .e_string => |e| { + e.resovleRopeIfNeeded(p.options.allocator); // If this was originally a template literal, print it as one as long as we're not minifying if (e.prefer_template) { @@ -1956,14 +1957,17 @@ pub fn NewPrinter( p.print("`"); if (e.head.isPresent()) { + e.head.resovleRopeIfNeeded(p.options.allocator); + p.printStringContent(&e.head, '`'); } - for (e.parts) |part| { + for (e.parts) |*part| { p.print("${"); p.printExpr(part.value, .lowest, ExprFlag.None()); p.print("}"); if (part.tail.isPresent()) { + part.tail.resovleRopeIfNeeded(p.options.allocator); p.printStringContent(&part.tail, '`'); } } @@ -2294,9 +2298,9 @@ pub fn NewPrinter( pub fn printStringContent(p: *Printer, str: *const E.String, c: u8) void { if (!str.isUTF8()) { // its already quoted for us! - p.printQuotedUTF16(str.value, c); + p.printQuotedUTF16(str.slice16(), c); } else { - p.printUTF8StringEscapedQuotes(str.utf8, c); + p.printUTF8StringEscapedQuotes(str.data, c); } } @@ -2479,6 +2483,7 @@ pub fn NewPrinter( .e_string => |key| { p.addSourceMapping(_key.loc); if (key.isUTF8()) { + key.resovleRopeIfNeeded(p.options.allocator); p.printSpaceBeforeIdentifier(); var allow_shorthand: bool = true; // In react/cjs/react.development.js, there's part of a function like this: @@ -2489,16 +2494,16 @@ pub fn NewPrinter( // While each of those property keys are ASCII, a subset of ASCII is valid as the start of an identifier // "=" and ":" are not valid // So we need to check - if ((comptime !is_json) and p.canPrintIdentifier(key.utf8)) { - p.print(key.utf8); + if ((comptime !is_json) and p.canPrintIdentifier(key.data)) { + p.print(key.data); } else { allow_shorthand = false; - const quote = p.bestQuoteCharForString(key.utf8, true); + const quote = p.bestQuoteCharForString(key.data, true); if (quote == '`') { p.print('['); } p.print(quote); - p.printUTF8StringEscapedQuotes(key.utf8, quote); + p.printUTF8StringEscapedQuotes(key.data, quote); p.print(quote); if (quote == '`') { p.print(']'); @@ -2521,7 +2526,7 @@ pub fn NewPrinter( .e_import_identifier => |e| { const ref = p.symbols.follow(e.ref); if (p.symbols.get(ref)) |symbol| { - if (symbol.namespace_alias == null and strings.eql(key.utf8, p.renamer.nameForSymbol(e.ref))) { + if (symbol.namespace_alias == null and strings.eql(key.data, p.renamer.nameForSymbol(e.ref))) { if (item.initializer) |initial| { p.printInitializer(initial); } @@ -2534,9 +2539,9 @@ pub fn NewPrinter( else => {}, } } - } else if (p.canPrintIdentifierUTF16(key.value)) { + } else if (p.canPrintIdentifierUTF16(key.slice16())) { p.printSpaceBeforeIdentifier(); - p.printIdentifierUTF16(key.value) catch unreachable; + p.printIdentifierUTF16(key.slice16()) catch unreachable; // Use a shorthand property if the names are the same if (item.value) |val| { @@ -2547,7 +2552,7 @@ pub fn NewPrinter( // esbuild doesn't have to do that... // maybe it's a symptom of some other underlying issue // or maybe, it's because i'm not lowering the same way that esbuild does. - if (item.flags.contains(.was_shorthand) or strings.utf16EqlString(key.value, p.renamer.nameForSymbol(e.ref))) { + if (item.flags.contains(.was_shorthand) or strings.utf16EqlString(key.slice16(), p.renamer.nameForSymbol(e.ref))) { if (item.initializer) |initial| { p.printInitializer(initial); } @@ -2558,7 +2563,7 @@ pub fn NewPrinter( .e_import_identifier => |e| { const ref = p.symbols.follow(e.ref); if (p.symbols.get(ref)) |symbol| { - if (symbol.namespace_alias == null and strings.utf16EqlString(key.value, p.renamer.nameForSymbol(e.ref))) { + if (symbol.namespace_alias == null and strings.utf16EqlString(key.slice16(), p.renamer.nameForSymbol(e.ref))) { if (item.initializer) |initial| { p.printInitializer(initial); } @@ -2570,9 +2575,9 @@ pub fn NewPrinter( } } } else { - const c = p.bestQuoteCharForString(key.value, false); + const c = p.bestQuoteCharForString(key.slice16(), false); p.print(c); - p.printQuotedUTF16(key.value, c); + p.printQuotedUTF16(key.slice16(), c); p.print(c); } }, @@ -2704,6 +2709,7 @@ pub fn NewPrinter( switch (property.key.data) { .e_string => |str| { + str.resovleRopeIfNeeded(p.options.allocator); p.addSourceMapping(property.key.loc); if (str.isUTF8()) { @@ -2714,8 +2720,8 @@ pub fn NewPrinter( // ^ // That needs to be: // "aria-label": ariaLabel, - if (p.canPrintIdentifier(str.utf8)) { - p.printIdentifier(str.utf8); + if (p.canPrintIdentifier(str.data)) { + p.printIdentifier(str.data); // Use a shorthand property if the names are the same switch (property.value.data) { @@ -2728,16 +2734,16 @@ pub fn NewPrinter( else => {}, } } else { - p.printQuotedUTF8(str.utf8, false); + p.printQuotedUTF8(str.data, false); } - } else if (p.canPrintIdentifierUTF16(str.value)) { + } else if (p.canPrintIdentifierUTF16(str.slice16())) { p.printSpaceBeforeIdentifier(); - p.printIdentifierUTF16(str.value) catch unreachable; + p.printIdentifierUTF16(str.slice16()) catch unreachable; // Use a shorthand property if the names are the same switch (property.value.data) { .b_identifier => |id| { - if (strings.utf16EqlString(str.value, p.renamer.nameForSymbol(id.ref))) { + if (strings.utf16EqlString(str.slice16(), p.renamer.nameForSymbol(id.ref))) { p.maybePrintDefaultBindingValue(property); continue; } @@ -2789,13 +2795,7 @@ pub fn NewPrinter( pub fn printStmt(p: *Printer, stmt: Stmt) !void { const prev_stmt_tag = p.prev_stmt_tag; - // Give an extra newline for readaiblity defer { - // - if (std.meta.activeTag(stmt.data) != .s_import and prev_stmt_tag == .s_import) { - p.printNewline(); - } - p.prev_stmt_tag = std.meta.activeTag(stmt.data); } @@ -2879,6 +2879,8 @@ pub fn NewPrinter( } }, .s_empty => { + if (p.prev_stmt_tag == .s_empty and p.options.indent == 0) return; + p.printIndent(); p.print(";"); p.printNewline(); diff --git a/src/json_parser.zig b/src/json_parser.zig index e6292488f..4bc442a5e 100644 --- a/src/json_parser.zig +++ b/src/json_parser.zig @@ -167,10 +167,7 @@ fn JSONLikeParser_( .t_string_literal => { var str: E.String = p.lexer.toEString(); if (comptime force_utf8) { - if (str.value.len > 0) { - str.utf8 = p.lexer.utf16ToString(str.value); - str.value = &[_]u16{}; - } + str.toUTF8(p.allocator) catch unreachable; } try p.lexer.next(); @@ -469,21 +466,21 @@ pub const PackageJSONVersionChecker = struct { // if you have multiple "name" fields in the package.json.... // first one wins if (key.data == .e_string and value.data == .e_string) { - if (!p.has_found_name and strings.eqlComptime(key.data.e_string.utf8, "name")) { + if (!p.has_found_name and strings.eqlComptime(key.data.e_string.data, "name")) { const len = @minimum( - value.data.e_string.utf8.len, + value.data.e_string.data.len, p.found_name_buf.len, ); - std.mem.copy(u8, &p.found_name_buf, value.data.e_string.utf8[0..len]); + std.mem.copy(u8, &p.found_name_buf, value.data.e_string.data[0..len]); p.found_name = p.found_name_buf[0..len]; p.has_found_name = true; - } else if (!p.has_found_version and strings.eqlComptime(key.data.e_string.utf8, "version")) { + } else if (!p.has_found_version and strings.eqlComptime(key.data.e_string.data, "version")) { const len = @minimum( - value.data.e_string.utf8.len, + value.data.e_string.data.len, p.found_version_buf.len, ); - std.mem.copy(u8, &p.found_version_buf, value.data.e_string.utf8[0..len]); + std.mem.copy(u8, &p.found_version_buf, value.data.e_string.data[0..len]); p.found_version = p.found_version_buf[0..len]; p.has_found_version = true; } @@ -570,7 +567,7 @@ pub fn toAST( }, .Slice => { if (ptr_info.child == u8) { - return Expr.init(js_ast.E.String, js_ast.E.String{ .utf8 = value }, logger.Loc.Empty); + return Expr.init(js_ast.E.String, js_ast.E.String.init(value), logger.Loc.Empty); } var exprs = try allocator.alloc(Expr, value.len); @@ -584,7 +581,7 @@ pub fn toAST( }, .Array => |Array| { if (Array.child == u8) { - return Expr.init(js_ast.E.String, js_ast.E.String{ .utf8 = value }, logger.Loc.Empty); + return Expr.init(js_ast.E.String, js_ast.E.String.init(value), logger.Loc.Empty); } var exprs = try allocator.alloc(Expr, value.len); @@ -600,7 +597,7 @@ pub fn toAST( var property_i: usize = 0; inline for (fields) |field| { properties[property_i] = G.Property{ - .key = Expr.init(E.String, E.String{ .utf8 = field.name }, logger.Loc.Empty), + .key = Expr.init(E.String, E.String{ .data = field.name }, logger.Loc.Empty), .value = try toAST(allocator, field.field_type, @field(value, field.name)), }; property_i += 1; @@ -697,7 +694,7 @@ const JSONParserForMacro = JSONLikeParser( var empty_object = E.Object{}; var empty_array = E.Array{}; -var empty_string = E.String{ .utf8 = "" }; +var empty_string = E.String{}; var empty_string_data = Expr.Data{ .e_string = &empty_string }; var empty_object_data = Expr.Data{ .e_object = &empty_object }; var empty_array_data = Expr.Data{ .e_array = &empty_array }; diff --git a/src/resolver/package_json.zig b/src/resolver/package_json.zig index dc02ad358..280b27258 100644 --- a/src/resolver/package_json.zig +++ b/src/resolver/package_json.zig @@ -356,7 +356,7 @@ pub const PackageJSON = struct { var count: usize = 0; const items = array.items.slice(); for (items) |item| { - count += @boolToInt(item.data == .e_string and item.data.e_string.utf8.len > 0); + count += @boolToInt(item.data == .e_string and item.data.e_string.data.len > 0); } switch (count) { 0 => {}, @@ -374,7 +374,7 @@ pub const PackageJSON = struct { var list_i: usize = 0; for (items) |item| { - if (item.data == .e_string and item.data.e_string.utf8.len > 0) { + if (item.data == .e_string and item.data.e_string.data.len > 0) { list[list_i] = item.data.e_string.string(allocator) catch unreachable; list_i += 1; } @@ -398,7 +398,7 @@ pub const PackageJSON = struct { while (array.next()) |expr| { if (expr.data != .e_string) continue; const e_str: *const js_ast.E.String = expr.data.e_string; - if (e_str.utf8.len == 0 or e_str.utf8[0] != '.') continue; + if (e_str.data.len == 0 or e_str.data[0] != '.') continue; valid_count += 1; } @@ -411,8 +411,8 @@ pub const PackageJSON = struct { while (array.next()) |expr| { if (expr.data != .e_string) continue; const e_str: *const js_ast.E.String = expr.data.e_string; - if (e_str.utf8.len == 0 or e_str.utf8[0] != '.') continue; - extensions[i] = e_str.utf8; + if (e_str.data.len == 0 or e_str.data[0] != '.') continue; + extensions[i] = e_str.data; i += 1; } } @@ -512,7 +512,7 @@ pub const PackageJSON = struct { for (remap_properties) |remap| { const import_name = remap.key.?.asString(allocator) orelse continue; const remap_value = remap.value.?; - if (remap_value.data != .e_string or remap_value.data.e_string.utf8.len == 0) { + if (remap_value.data != .e_string or remap_value.data.e_string.data.len == 0) { log.addWarningFmt( json_source, remap_value.loc, @@ -523,7 +523,7 @@ pub const PackageJSON = struct { continue; } - const remap_value_str = remap_value.data.e_string.utf8; + const remap_value_str = remap_value.data.e_string.data; map.putAssumeCapacityNoClobber(import_name, remap_value_str); } diff --git a/src/runtime.zig b/src/runtime.zig index 4b4a5a3e2..c79bf328f 100644 --- a/src/runtime.zig +++ b/src/runtime.zig @@ -275,6 +275,7 @@ pub const Runtime = struct { allow_runtime: bool = true, trim_unused_imports: bool = false, + should_fold_numeric_constants: bool = false, replace_exports: ReplaceableExport.Map = .{}, diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 6114c7e06..a928f45a6 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -5,6 +5,7 @@ const string = @import("string_types.zig").string; const stringZ = @import("string_types.zig").stringZ; const CodePoint = @import("string_types.zig").CodePoint; const bun = @import("global.zig"); +pub const joiner = @import("./string_joiner.zig"); const assert = std.debug.assert; pub inline fn containsChar(self: string, char: u8) bool { return indexOfChar(self, char) != null; diff --git a/src/toml/toml_lexer.zig b/src/toml/toml_lexer.zig index fac1016d6..d54b64863 100644 --- a/src/toml/toml_lexer.zig +++ b/src/toml/toml_lexer.zig @@ -1160,7 +1160,7 @@ pub const Lexer = struct { } pub inline fn toEString(lexer: *Lexer) js_ast.E.String { - return js_ast.E.String{ .utf8 = lexer.string_literal_slice }; + return js_ast.E.String{ .data = lexer.string_literal_slice }; } pub fn raw(self: *Lexer) []const u8 { diff --git a/src/toml/toml_parser.zig b/src/toml/toml_parser.zig index 6673d5c41..cdfd49f5a 100644 --- a/src/toml/toml_parser.zig +++ b/src/toml/toml_parser.zig @@ -114,7 +114,7 @@ pub const TOML = struct { return p.e(str, loc); }, .t_identifier => { - const str = E.String{ .utf8 = p.lexer.identifier }; + const str = E.String{ .data = p.lexer.identifier }; try p.lexer.next(); return p.e(str, loc); }, @@ -122,7 +122,7 @@ pub const TOML = struct { try p.lexer.next(); return p.e( E.String{ - .utf8 = "false", + .data = "false", }, loc, ); @@ -131,7 +131,7 @@ pub const TOML = struct { try p.lexer.next(); return p.e( E.String{ - .utf8 = "true", + .data = "true", }, loc, ); @@ -140,7 +140,7 @@ pub const TOML = struct { .t_numeric_literal => { const literal = p.lexer.raw(); try p.lexer.next(); - return p.e(E.String{ .utf8 = literal }, loc); + return p.e(E.String{ .data = literal }, loc); }, else => return null, @@ -298,7 +298,7 @@ pub const TOML = struct { return p.e(str, loc); }, .t_identifier => { - var str: E.String = E.String{ .utf8 = p.lexer.identifier }; + var str: E.String = E.String{ .data = p.lexer.identifier }; try p.lexer.next(); return p.e(str, loc); |