From f912e0abc402391703f45afffb3f770ca74dc165 Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Sat, 8 Feb 2025 00:31:30 -0800 Subject: [PATCH 01/25] hot module reloading for HTML import development mode (#16955) --- .gitignore | 1 + build.zig | 1 + bun.lock | 66 +- cmake/targets/BuildBun.cmake | 28 +- cmake/tools/SetupBun.cmake | 8 + package.json | 8 +- packages/bun-types/bun.d.ts | 9 +- src/HTMLScanner.zig | 82 +- src/Watcher.zig | 46 +- src/api/schema.zig | 2 +- src/ast/base.zig | 2 +- src/bake/BakeGlobalObject.cpp | 17 +- src/bake/DevServer.zig | 3060 +++++++++++++---- src/bake/FrameworkRouter.zig | 14 +- src/bake/bake.d.ts | 4 +- src/bake/bake.private.d.ts | 3 + src/bake/bake.zig | 86 +- src/bake/bun-framework-react/client.tsx | 7 - src/bake/client/css-reloader.ts | 6 +- src/bake/client/overlay.css | 10 +- src/bake/client/overlay.ts | 37 +- src/bake/client/reader.ts | 4 +- src/bake/client/route.ts | 6 - src/bake/client/websocket.ts | 76 +- src/bake/hmr-module.ts | 113 +- src/bake/hmr-runtime-client.ts | 41 +- src/bake/hmr-runtime-error.ts | 25 +- src/bake/hmr-runtime-server.ts | 16 +- src/bake/incremental_visualizer.html | 60 +- src/bake/macros.ts | 21 +- src/bake/production.zig | 32 +- src/bit_set.zig | 5 + src/bun.js/api/bun/dns_resolver.zig | 32 +- src/bun.js/api/server.zig | 407 ++- src/bun.js/api/server/HTMLBundle.zig | 301 +- src/bun.js/api/server/StaticRoute.zig | 89 +- src/bun.js/bindings/BunProcess.cpp | 2 +- src/bun.js/bindings/BunProcess.h | 3 +- src/bun.js/bindings/bindings.zig | 8 +- src/bun.js/module_loader.zig | 4 +- src/bun.js/webcore/blob.zig | 39 +- src/bun.js/webcore/response.zig | 15 +- src/bun.zig | 34 +- src/bundler/bundle_v2.zig | 647 ++-- src/cache.zig | 18 +- src/cli.zig | 5 + src/cli/build_command.zig | 20 +- src/cli/create_command.zig | 14 +- src/cli/install.ps1 | 2 +- src/cli/upgrade_command.zig | 20 +- src/codegen/bake-codegen.ts | 4 +- src/crash_handler.zig | 6 + src/deps/libuv.zig | 3 +- src/deps/lol-html.zig | 4 +- src/deps/uws.zig | 24 +- src/feature_flags.zig | 8 +- src/fmt.zig | 10 +- src/fs.zig | 8 +- src/hive_array.zig | 16 +- src/http.zig | 16 +- src/http/header_builder.zig | 10 +- src/http/headers.zig | 8 - src/http/mime_type.zig | 5 + src/import_record.zig | 2 - src/install/install.zig | 4 +- src/js_ast.zig | 16 +- src/js_parser.zig | 454 ++- src/js_printer.zig | 95 +- src/node-fallbacks/bun.lock | 45 +- src/node-fallbacks/package.json | 4 +- src/renamer.zig | 4 +- src/resolver/resolve_path.zig | 298 +- src/resolver/resolver.zig | 7 + src/sourcemap/sourcemap.zig | 22 +- src/sys.zig | 9 + src/watcher.zig | 670 ---- src/watcher/INotifyWatcher.zig | 2 +- src/watcher/KEventWatcher.zig | 18 +- .../test/__snapshots__/coverage.test.ts.snap | 4 +- test/js/bun/http/bun-serve-html-entry.test.ts | 16 +- test/js/bun/http/bun-serve-html.test.ts | 34 +- test/js/bun/util/inspect-error.test.js | 44 +- 82 files changed, 4461 insertions(+), 2965 deletions(-) delete mode 100644 src/bake/client/route.ts delete mode 100644 src/http/headers.zig delete mode 100644 src/watcher.zig diff --git a/.gitignore b/.gitignore index b791b8bc3a047f..e8a8c92992968e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ .vscode/clang* .vscode/cpp* .zig-cache +.bake-debug *.a *.bc *.big diff --git a/build.zig b/build.zig index 054fc4dd944395..ce8624f7e5306c 100644 --- a/build.zig +++ b/build.zig @@ -561,6 +561,7 @@ fn addInternalPackages(b: *Build, obj: *Compile, opts: *BunBuildOptions) void { .{ .file = "bun-error/index.js", .enable = opts.shouldEmbedCode() }, .{ .file = "bun-error/bun-error.css", .enable = opts.shouldEmbedCode() }, .{ .file = "fallback-decoder.js", .enable = opts.shouldEmbedCode() }, + .{ .file = "node-fallbacks/react-refresh.js", .enable = opts.shouldEmbedCode() }, .{ .file = "node-fallbacks/assert.js", .enable = opts.shouldEmbedCode() }, .{ .file = "node-fallbacks/buffer.js", .enable = opts.shouldEmbedCode() }, .{ .file = "node-fallbacks/console.js", .enable = opts.shouldEmbedCode() }, diff --git a/bun.lock b/bun.lock index 4729944dcf9be9..81b4fa1253a77d 100644 --- a/bun.lock +++ b/bun.lock @@ -10,8 +10,8 @@ "@typescript-eslint/eslint-plugin": "^7.11.0", "@typescript-eslint/parser": "^7.11.0", "@vscode/debugadapter": "^1.65.0", - "autoprefixer": "^10.4.20", - "caniuse-lite": "^1.0.30001660", + "autoprefixer": "^10.4.19", + "caniuse-lite": "^1.0.30001620", "esbuild": "^0.21.4", "eslint": "^9.4.0", "eslint-config-prettier": "^9.1.0", @@ -45,21 +45,21 @@ "packages": { "@biomejs/biome": ["@biomejs/biome@1.8.3", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.8.3", "@biomejs/cli-darwin-x64": "1.8.3", "@biomejs/cli-linux-arm64": "1.8.3", "@biomejs/cli-linux-arm64-musl": "1.8.3", "@biomejs/cli-linux-x64": "1.8.3", "@biomejs/cli-linux-x64-musl": "1.8.3", "@biomejs/cli-win32-arm64": "1.8.3", "@biomejs/cli-win32-x64": "1.8.3" }, "bin": { "biome": "bin/biome" } }, "sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.8.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.8.3", "", { "os":"darwin", "cpu":"arm64" }, "sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.8.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.8.3", "", { "os":"darwin", "cpu":"x64" }, "sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.8.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.8.3", "", { "os":"linux", "cpu":"arm64" }, "sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.8.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.8.3", "", { "os":"linux", "cpu":"arm64" }, "sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.8.3", "", { "os": "linux", "cpu": "x64" }, "sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.8.3", "", { "os":"linux", "cpu":"x64" }, "sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.8.3", "", { "os": "linux", "cpu": "x64" }, "sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.8.3", "", { "os":"linux", "cpu":"x64" }, "sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.8.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.8.3", "", { "os":"win32", "cpu":"arm64" }, "sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.8.3", "", { "os": "win32", "cpu": "x64" }, "sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.8.3", "", { "os":"win32", "cpu":"x64" }, "sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg=="], "@definitelytyped/dts-critic": ["@definitelytyped/dts-critic@0.0.191", "", { "dependencies": { "@definitelytyped/header-parser": "0.0.190", "command-exists": "^1.2.9", "semver": "^7.5.4", "tmp": "^0.2.1", "typescript": "^5.2.2", "yargs": "^17.7.2" } }, "sha512-j5HK3pQYiQwSXRLJzyhXJ6KxdzLl4gXXhz3ysCtLnRQkj+zsEfloDkEZ3x2bZMWS0OsKLXmR91JeQ2/c9DFEjg=="], @@ -75,51 +75,51 @@ "@es-joy/jsdoccomment": ["@es-joy/jsdoccomment@0.39.4", "", { "dependencies": { "comment-parser": "1.3.1", "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" } }, "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os":"aix", "cpu":"ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os":"android", "cpu":"arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os":"android", "cpu":"arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os":"android", "cpu":"x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os":"darwin", "cpu":"arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os":"darwin", "cpu":"x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os":"freebsd", "cpu":"arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os":"freebsd", "cpu":"x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os":"linux", "cpu":"arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os":"linux", "cpu":"arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os":"linux", "cpu":"ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os":"linux", "cpu":"none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os":"linux", "cpu":"none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os":"linux", "cpu":"ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os":"linux", "cpu":"none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os":"linux", "cpu":"s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os":"linux", "cpu":"x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os":"none", "cpu":"x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os":"openbsd", "cpu":"x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os":"sunos", "cpu":"x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os":"win32", "cpu":"arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os":"win32", "cpu":"ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os":"win32", "cpu":"x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.4.0", "", { "dependencies": { "eslint-visitor-keys": "^3.3.0" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA=="], diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake index 3eaf1b566d2fdd..2e7df7b43aff08 100644 --- a/cmake/targets/BuildBun.cmake +++ b/cmake/targets/BuildBun.cmake @@ -179,6 +179,32 @@ register_command( ${BUN_NODE_FALLBACKS_OUTPUTS} ) +# An embedded copy of react-refresh is used when the user forgets to install it. +# The library is not versioned alongside React. +set(BUN_REACT_REFRESH_OUTPUT ${BUN_NODE_FALLBACKS_OUTPUT}/react-refresh.js) +register_command( + TARGET + bun-node-fallbacks-react-refresh + COMMENT + "Building node-fallbacks/react-refresh.js" + CWD + ${BUN_NODE_FALLBACKS_SOURCE} + COMMAND + ${BUN_EXECUTABLE} build + ${BUN_NODE_FALLBACKS_SOURCE}/node_modules/react-refresh/cjs/react-refresh-runtime.development.js + --outfile=${BUN_REACT_REFRESH_OUTPUT} + --target=browser + --format=cjs + --minify + --define:process.env.NODE_ENV=\"'development'\" + SOURCES + ${BUN_NODE_FALLBACKS_SOURCE}/package.json + ${BUN_NODE_FALLBACKS_SOURCE}/bun.lock + ${BUN_NODE_FALLBACKS_NODE_MODULES} + OUTPUTS + ${BUN_REACT_REFRESH_OUTPUT} +) + set(BUN_ERROR_CODE_SCRIPT ${CWD}/src/codegen/generate-node-errors.ts) set(BUN_ERROR_CODE_SOURCES @@ -510,6 +536,7 @@ set(BUN_ZIG_GENERATED_SOURCES ${BUN_FALLBACK_DECODER_OUTPUT} ${BUN_RUNTIME_JS_OUTPUT} ${BUN_NODE_FALLBACKS_OUTPUTS} + ${BUN_REACT_REFRESH_OUTPUT} ${BUN_ERROR_CODE_OUTPUTS} ${BUN_ZIG_GENERATED_CLASSES_OUTPUTS} ${BUN_JAVASCRIPT_OUTPUTS} @@ -567,7 +594,6 @@ register_command( -Dcanary=${CANARY_REVISION} -Dcodegen_path=${CODEGEN_PATH} -Dcodegen_embed=$,true,false> - -Denable_asan=$,true,false> --prominent-compile-errors ${ZIG_FLAGS_BUN} ARTIFACTS diff --git a/cmake/tools/SetupBun.cmake b/cmake/tools/SetupBun.cmake index 5377eb1cff1a19..3cb77ff4be9e3b 100644 --- a/cmake/tools/SetupBun.cmake +++ b/cmake/tools/SetupBun.cmake @@ -9,6 +9,14 @@ find_command( >=1.1.26 ) +if (NOT CI) + # If node.js is not installed, it is extremely easy to make this path point to + # a tempdir such as /private/tmp/bun-node-ce532901c/bun, which may cause this + # CMake configuration break after tempdir is cleaned up (ex. after reboot). + get_filename_component(BUN_EXECUTABLE ${BUN_EXECUTABLE} REALPATH) + set(BUN_EXECUTABLE ${BUN_EXECUTABLE} CACHE FILEPATH "Bun executable" FORCE) +endif() + # If this is not set, some advanced features are not checked. # https://github.com/oven-sh/bun/blob/cd7f6a1589db7f1e39dc4e3f4a17234afbe7826c/src/bun.js/javascript.zig#L1069-L1072 setenv(BUN_GARBAGE_COLLECTOR_LEVEL 1) diff --git a/package.json b/package.json index 45c9ed7b50a362..e064be915a6d10 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,14 @@ "./packages/bun-types" ], "devDependencies": { + "@mdn/browser-compat-data": "~5.5.28", "@types/bun": "*", "@types/react": "^18.3.3", "@typescript-eslint/eslint-plugin": "^7.11.0", "@typescript-eslint/parser": "^7.11.0", "@vscode/debugadapter": "^1.65.0", + "autoprefixer": "^10.4.19", + "caniuse-lite": "^1.0.30001620", "esbuild": "^0.21.4", "eslint": "^9.4.0", "eslint-config-prettier": "^9.1.0", @@ -21,10 +24,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "source-map-js": "^1.2.0", - "typescript": "^5.7.2", - "caniuse-lite": "^1.0.30001660", - "autoprefixer": "^10.4.20", - "@mdn/browser-compat-data": "~5.5.28" + "typescript": "^5.7.2" }, "resolutions": { "bun-types": "workspace:packages/bun-types" diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index 8d11ef1a943015..7195f0252412ce 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -5325,7 +5325,10 @@ declare module "bun" { interface PluginBuilder { /** - * Register a callback which will be invoked when bundling starts. + * Register a callback which will be invoked when bundling starts. When + * using hot module reloading, this is called at the start of each + * incremental rebuild. + * * @example * ```ts * Bun.plugin({ @@ -5422,9 +5425,9 @@ declare module "bun" { * - `browser`: The plugin will be applied to browser builds * - `node`: The plugin will be applied to Node.js builds * - * If in Bun's runtime, the default target is `bun`. + * If unspecified, it is assumed that the plugin is compatible with all targets. * - * If unspecified, it is assumed that the plugin is compatible with the default target. + * This field is not read by Bun.plugin */ target?: Target; /** diff --git a/src/HTMLScanner.zig b/src/HTMLScanner.zig index b029e3dd46f6c6..4a69da3574af6c 100644 --- a/src/HTMLScanner.zig +++ b/src/HTMLScanner.zig @@ -56,9 +56,8 @@ fn createImportRecord(this: *HTMLScanner, input_path: []const u8, kind: ImportKi const debug = bun.Output.scoped(.HTMLScanner, true); -pub fn onWriteHTML(this: *HTMLScanner, bytes: []const u8) void { - _ = this; // autofix - _ = bytes; // autofix +pub fn onWriteHTML(_: *HTMLScanner, bytes: []const u8) void { + _ = bytes; // bytes are not written in scan phase } pub fn onHTMLParseError(this: *HTMLScanner, message: []const u8) void { @@ -70,7 +69,7 @@ pub fn onHTMLParseError(this: *HTMLScanner, message: []const u8) void { } pub fn onTag(this: *HTMLScanner, _: *lol.Element, path: []const u8, url_attribute: []const u8, kind: ImportKind) void { - _ = url_attribute; // autofix + _ = url_attribute; this.createImportRecord(path, kind) catch {}; } @@ -80,7 +79,7 @@ pub fn scan(this: *HTMLScanner, input: []const u8) !void { try processor.run(this, input); } -pub fn HTMLProcessor(comptime T: type, comptime add_head_or_html_tag: bool) type { +pub fn HTMLProcessor(comptime T: type, comptime visit_head_and_body: bool) type { return struct { const TagHandler = struct { /// CSS selector to match elements @@ -95,7 +94,7 @@ pub fn HTMLProcessor(comptime T: type, comptime add_head_or_html_tag: bool) type is_head_or_html: bool = false, }; - const tag_handlers_ = [_]TagHandler{ + const tag_handlers = [_]TagHandler{ // Module scripts with src .{ .selector = "script[src]", @@ -208,16 +207,6 @@ pub fn HTMLProcessor(comptime T: type, comptime add_head_or_html_tag: bool) type // }, }; - const html_head_tag_handler: TagHandler = .{ - .selector = "head", - .has_content = false, - .url_attribute = "", - .kind = .stmt, - .is_head_or_html = true, - }; - - const tag_handlers = if (add_head_or_html_tag) tag_handlers_ ++ [_]TagHandler{html_head_tag_handler} else tag_handlers_; - fn generateHandlerForTag(comptime tag_info: TagHandler) fn (*T, *lol.Element) bool { const Handler = struct { pub fn handle(this: *T, element: *lol.Element) bool { @@ -232,13 +221,6 @@ pub fn HTMLProcessor(comptime T: type, comptime add_head_or_html_tag: bool) type } } } - - if (comptime add_head_or_html_tag) { - if (tag_info.is_head_or_html) { - T.onHEADTag(this, element); - } - } - return false; } }; @@ -248,18 +230,16 @@ pub fn HTMLProcessor(comptime T: type, comptime add_head_or_html_tag: bool) type pub fn run(this: *T, input: []const u8) !void { var builder = lol.HTMLRewriter.Builder.init(); defer builder.deinit(); - var selectors = try std.ArrayList(*lol.HTMLSelector).initCapacity(this.allocator, tag_handlers.len); - defer { - for (selectors.items) |selector| { - selector.deinit(); - } - selectors.deinit(); - } + + var selectors: std.BoundedArray(*lol.HTMLSelector, tag_handlers.len + if (visit_head_and_body) 2 else 0) = .{}; + defer for (selectors.slice()) |selector| { + selector.deinit(); + }; + // Add handlers for each tag type inline for (tag_handlers) |tag_info| { const selector = try lol.HTMLSelector.parse(tag_info.selector); - try selectors.append(selector); - + selectors.appendAssumeCapacity(selector); try builder.addElementContentHandlers( selector, T, @@ -274,6 +254,38 @@ pub fn HTMLProcessor(comptime T: type, comptime add_head_or_html_tag: bool) type ); } + if (visit_head_and_body) { + const head_selector = try lol.HTMLSelector.parse("head"); + selectors.appendAssumeCapacity(head_selector); + try builder.addElementContentHandlers( + head_selector, + T, + T.onHeadTag, + this, + void, + null, + null, + void, + null, + null, + ); + + const body_selector = try lol.HTMLSelector.parse("body"); + selectors.appendAssumeCapacity(body_selector); + try builder.addElementContentHandlers( + body_selector, + T, + T.onBodyTag, + this, + void, + null, + null, + void, + null, + null, + ); + } + const memory_settings = lol.MemorySettings{ .preallocated_parsing_buffer_size = @max(input.len / 4, 1024), .max_allowed_memory_usage = 1024 * 1024 * 10, @@ -294,11 +306,7 @@ pub fn HTMLProcessor(comptime T: type, comptime add_head_or_html_tag: bool) type false, T, this, - struct { - fn write(self: *T, bytes: []const u8) void { - self.onWriteHTML(bytes); - } - }.write, + T.onWriteHTML, struct { fn done(_: *T) void {} }.done, diff --git a/src/Watcher.zig b/src/Watcher.zig index 3f710c64ceb354..ae0be0426810be 100644 --- a/src/Watcher.zig +++ b/src/Watcher.zig @@ -1,17 +1,11 @@ //! Bun's cross-platform filesystem watcher. Runs on its own thread. const Watcher = @This(); -pub const max_count = 128; - -pub const Event = WatchEvent; -pub const Item = WatchItem; -pub const ItemList = WatchList; -pub const WatchList = std.MultiArrayList(WatchItem); -pub const HashType = u32; -const no_watch_item: WatchItemIndex = std.math.maxInt(WatchItemIndex); +const DebugLogScope = bun.Output.Scoped(.watcher, false); +const log = DebugLogScope.log; // Consumer-facing -watch_events: [128]WatchEvent, -changed_filepaths: [128]?[:0]u8, +watch_events: [max_count]WatchEvent, +changed_filepaths: [max_count]?[:0]u8, /// The platform-specific implementation of the watcher platform: Platform, @@ -37,6 +31,15 @@ onError: *const fn (this: *anyopaque, err: bun.sys.Error) void, thread_lock: bun.DebugThreadLock = bun.DebugThreadLock.unlocked, +pub const max_count = 128; + +pub const Event = WatchEvent; +pub const Item = WatchItem; +pub const ItemList = WatchList; +pub const WatchList = std.MultiArrayList(WatchItem); +pub const HashType = u32; +const no_watch_item: WatchItemIndex = std.math.maxInt(WatchItemIndex); + /// Initializes a watcher. Each watcher is tied to some context type, which /// recieves watch callbacks on the watcher thread. This function does not /// actually start the watcher thread. @@ -68,7 +71,7 @@ pub fn init(comptime T: type, ctx: *T, fs: *bun.fs.FileSystem, allocator: std.me const watcher = try allocator.create(Watcher); errdefer allocator.destroy(watcher); - watcher.* = Watcher{ + watcher.* = .{ .fs = fs, .allocator = allocator, .watched_count = 0, @@ -80,7 +83,7 @@ pub fn init(comptime T: type, ctx: *T, fs: *bun.fs.FileSystem, allocator: std.me .onError = &wrapped.onErrorWrapped, .platform = .{}, .watch_events = undefined, - .changed_filepaths = [_]?[:0]u8{null} ** 128, + .changed_filepaths = [_]?[:0]u8{null} ** max_count, }; try Platform.init(&watcher.platform, fs.top_level_dir); @@ -118,9 +121,6 @@ pub fn getHash(filepath: string) HashType { pub const WatchItemIndex = u16; pub const max_eviction_count = 8096; - -const log = bun.Output.scoped(.watcher, false); - const WindowsWatcher = @import("./watcher/WindowsWatcher.zig"); // TODO: some platform-specific behavior is implemented in // this file instead of the platform-specific file. @@ -214,7 +214,7 @@ fn threadMain(this: *Watcher) !void { Output.Source.configureNamedThread("File Watcher"); defer Output.flush(); - if (FeatureFlags.verbose_watcher) Output.prettyln("Watcher started", .{}); + log("Watcher started", .{}); switch (this.watchLoop()) { .err => |err| { @@ -534,12 +534,14 @@ pub fn appendFileMaybeLock( .result => {}, } - if (comptime FeatureFlags.verbose_watcher) { - if (strings.indexOf(file_path, this.cwd)) |i| { - Output.prettyln("Added ./{s} to watch list.", .{file_path[i + this.cwd.len ..]}); - } else { - Output.prettyln("Added {s} to watch list.", .{file_path}); - } + if (DebugLogScope.isVisible()) { + const cwd_len_with_slash = if (this.cwd[this.cwd.len - 1] == '/') this.cwd.len else this.cwd.len + 1; + log("Added {s} to watch list.", .{ + if (file_path.len > cwd_len_with_slash and bun.strings.startsWith(file_path, this.cwd)) + file_path[cwd_len_with_slash..] + else + file_path, + }); } return .{ .result = {} }; diff --git a/src/api/schema.zig b/src/api/schema.zig index 163a1a76131aeb..f7a36f2df42f2e 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -1678,7 +1678,7 @@ pub const Api = struct { no_summary: ?bool = null, /// disable_hmr - disable_hmr: ?bool = null, + disable_hmr: bool = false, /// port port: ?u16 = null, diff --git a/src/ast/base.zig b/src/ast/base.zig index de1f8a5a3f3d20..b1cc26244d139b 100644 --- a/src/ast/base.zig +++ b/src/ast/base.zig @@ -188,7 +188,7 @@ pub const Ref = packed struct(u64) { return this.tag == .source_contents_slice; } - pub fn init(inner_index: Int, source_index: usize, is_source_contents_slice: bool) Ref { + pub fn init(inner_index: Int, source_index: u32, is_source_contents_slice: bool) Ref { return .{ .inner_index = inner_index, .source_index = @intCast(source_index), diff --git a/src/bake/BakeGlobalObject.cpp b/src/bake/BakeGlobalObject.cpp index efa663385904d3..abcaf83f6e03a0 100644 --- a/src/bake/BakeGlobalObject.cpp +++ b/src/bake/BakeGlobalObject.cpp @@ -58,17 +58,18 @@ JSC::Identifier bakeModuleLoaderResolve(JSC::JSGlobalObject* jsGlobal, auto& vm = JSC::getVM(global); auto scope = DECLARE_THROW_SCOPE(vm); - ASSERT(referrer.isString()); - WTF::String refererString = jsCast(referrer)->getString(global); + if (auto string = jsDynamicCast(referrer)) { + WTF::String refererString = string->getString(global); - WTF::String keyString = key.toWTFString(global); - RETURN_IF_EXCEPTION(scope, vm.propertyNames->emptyIdentifier); - - if (refererString.startsWith("bake:/"_s) || (refererString == "."_s && keyString.startsWith("bake:/"_s))) { - BunString result = BakeProdResolve(global, Bun::toString(referrer.getString(global)), Bun::toString(keyString)); + WTF::String keyString = key.toWTFString(global); RETURN_IF_EXCEPTION(scope, vm.propertyNames->emptyIdentifier); - return JSC::Identifier::fromString(vm, result.toWTFString(BunString::ZeroCopy)); + if (refererString.startsWith("bake:/"_s) || (refererString == "."_s && keyString.startsWith("bake:/"_s))) { + BunString result = BakeProdResolve(global, Bun::toString(referrer.getString(global)), Bun::toString(keyString)); + RETURN_IF_EXCEPTION(scope, vm.propertyNames->emptyIdentifier); + + return JSC::Identifier::fromString(vm, result.toWTFString(BunString::ZeroCopy)); + } } // Use Zig::GlobalObject's function diff --git a/src/bake/DevServer.zig b/src/bake/DevServer.zig index 6b7f957926d542..25475232e96945 100644 --- a/src/bake/DevServer.zig +++ b/src/bake/DevServer.zig @@ -6,10 +6,15 @@ //! adjusting imports) must always rebundle only that one file. //! //! All work is held in-memory, using manually managed data-oriented design. +//! For questions about DevServer, please consult the delusional @paperclover pub const DevServer = @This(); -pub const debug = bun.Output.Scoped(.Bake, false); +pub const debug = bun.Output.Scoped(.DevServer, false); +pub const memoryLog = bun.Output.Scoped(.DevServerMemory, true); pub const igLog = bun.Output.scoped(.IncrementalGraph, false); +/// --no-hmr sets this to false +pub var enabled = true; + pub const Options = struct { /// Arena must live until DevServer.deinit() arena: Allocator, @@ -17,20 +22,23 @@ pub const Options = struct { vm: *VirtualMachine, framework: bake.Framework, bundler_options: bake.SplitBundlerOptions, + /// When set, nothing is ever bundled for the server-side, + /// and DevSever acts purely as a frontend bundler. + frontend_only: bool = false, // Debugging features dump_sources: ?[]const u8 = if (Environment.isDebug) ".bake-debug" else null, dump_state_on_crash: ?bool = null, - verbose_watcher: bool = false, }; -// The fields `client_graph`, `server_graph`, and `directory_watchers` all -// use `@fieldParentPointer` to access DevServer's state. This pattern has -// made it easier to group related fields together, but one must remember -// those structures still depend on the DevServer pointer. +// The fields `client_graph`, `server_graph`, `directory_watchers`, and `assets` +// all use `@fieldParentPointer` to access DevServer's state. This pattern has +// made it easier to group related fields together, but one must remember those +// structures still depend on the DevServer pointer. /// Used for all server-wide allocations. In debug, this shows up in /// a separate named heap. Thread-safe. +// TODO: make this an "AllocationScope" (debug memory tool i've yet to write) allocator: Allocator, /// Absolute path to project root directory. For the HMR /// runtime, its module IDs are strings relative to this. @@ -41,7 +49,9 @@ root: []const u8, configuration_hash_key: [16]u8, /// The virtual machine (global object) to execute code in. vm: *VirtualMachine, -/// May be `null` if not attached to an HTTP server yet. +/// May be `null` if not attached to an HTTP server yet. When no server is +/// available, functions taking in requests and responses are unavailable. +/// However, a lot of testing in this mode is missing, so it may hit assertions. server: ?bun.JSC.API.AnyServer, /// Contains the tree of routes. This structure contains FileIndex router: FrameworkRouter, @@ -55,18 +65,16 @@ client_graph: IncrementalGraph(.client), server_graph: IncrementalGraph(.server), /// State populated during bundling and hot updates. Often cleared incremental_result: IncrementalResult, -/// Quickly retrieve a route's index from its entry point file. These are -/// populated as the routes are discovered. The route may not be bundled OR +/// Quickly retrieve a framework route's index from its entry point file. These +/// are populated as the routes are discovered. The route may not be bundled OR /// navigatable, such as the case where a layout's index is looked up. route_lookup: AutoArrayHashMapUnmanaged(IncrementalGraph(.server).FileIndex, RouteIndexAndRecurseFlag), -/// CSS files are accessible via `/_bun/css/.css` -/// Value is bundled code owned by `dev.allocator` -css_files: AutoArrayHashMapUnmanaged(u64, []const u8), -/// JS files are accessible via `/_bun/client/route..js` -/// These are randomly generated to avoid possible browser caching of old assets. -route_js_payloads: AutoArrayHashMapUnmanaged(u64, Route.Index.Optional), -// /// Assets are accessible via `/_bun/asset/` -// assets: bun.StringArrayHashMapUnmanaged(u64, Asset), +/// This acts as a duplicate of the lookup table in uws, but only for HTML routes +/// Used to identify what route a connected WebSocket is on, so that only +/// the active pages are notified of a hot updates. +html_router: HTMLRouter, +/// Assets are accessible via `/_bun/asset/` +assets: Assets, /// All bundling failures are stored until a file is saved and rebuilt. /// They are stored in the wire format the HMR runtime expects so that /// serialization only happens once. @@ -76,6 +84,11 @@ bundling_failures: std.ArrayHashMapUnmanaged( SerializedFailure.ArrayHashContextViaOwner, false, ) = .{}, +frontend_only: bool, +/// The Plugin API is missing a way to attach filesystem watchers (addWatchFile) +/// This special case makes `bun-plugin-tailwind` work, which is a requirement +/// to ship initial incremental bundling support for HTML files. +has_tailwind_plugin_hack: ?bun.StringArrayHashMapUnmanaged(void) = null, // These values are handles to the functions in `hmr-runtime-server.ts`. // For type definitions, see `./bake.private.d.ts` @@ -96,32 +109,46 @@ bundles_since_last_error: usize = 0, framework: bake.Framework, bundler_options: bake.SplitBundlerOptions, // Each logical graph gets its own bundler configuration -server_bundler: Transpiler, -client_bundler: Transpiler, -ssr_bundler: Transpiler, -/// The log used by all `server_bundler`, `client_bundler` and `ssr_bundler`. +server_transpiler: Transpiler, +client_transpiler: Transpiler, +ssr_transpiler: Transpiler, +/// The log used by all `server_transpiler`, `client_transpiler` and `ssr_transpiler`. /// Note that it is rarely correct to write messages into it. Instead, associate /// messages with the IncrementalGraph file or Route using `SerializedFailure` log: Log, +plugin_state: enum { + /// Should ask server for plugins. Once plugins are loaded, the plugin + /// pointer is written into `server_transpiler.options.plugin` + unknown, + // These two states mean that `server.getOrLoadPlugins()` was called. + pending, + loaded, + /// Currently, this represents a degraded state where no bundle can + /// be correctly executed because the plugins did not load successfully. + err, +}, /// There is only ever one bundle executing at the same time, since all bundles /// inevitably share state. This bundle is asynchronous, storing its state here /// while in-flight. All allocations held by `.bv2.graph.heap`'s arena current_bundle: ?struct { bv2: *BundleV2, /// Information BundleV2 needs to finalize the bundle - start_data: bun.bundle_v2.BakeBundleStart, + start_data: bun.bundle_v2.DevServerInput, /// Started when the bundle was queued timer: std.time.Timer, /// If any files in this bundle were due to hot-reloading, some extra work /// must be done to inform clients to reload routes. When this is false, /// all entry points do not have bundles yet. had_reload_event: bool, + /// After a bundle finishes, these requests will be continued, either + /// calling their handler on success or sending the error page on failure. + /// Owned by `deferred_request_pool` in DevServer. + requests: DeferredRequest.List, + /// Resolution failures are grouped by incremental graph file index. + /// Unlike parse failures (`handleParseTaskFailure`), the resolution + /// failures can be created asynchronously, and out of order. + resolution_failure_entries: AutoArrayHashMapUnmanaged(SerializedFailure.Owner.Packed, bun.logger.Log), }, -/// This is not stored in `current_bundle` so that its memory can be reused when -/// there is no active bundle. After the bundle finishes, these requests will -/// be continued, either calling their handler on success or sending the error -/// page on failure. -current_bundle_requests: ArrayListUnmanaged(DeferredRequest), /// When `current_bundle` is non-null and new requests to bundle come in, /// those are temporaried here. When the current bundle is finished, it /// will immediately enqueue this. @@ -132,8 +159,9 @@ next_bundle: struct { /// for this watch event is in one of the `watch_events` reload_event: ?*HotReloadEvent, /// The list of requests that are blocked on this bundle. - requests: ArrayListUnmanaged(DeferredRequest), + requests: DeferredRequest.List, }, +deferred_request_pool: bun.HiveArray(DeferredRequest.Node, DeferredRequest.max_preallocated).Fallback, // Debugging @@ -143,50 +171,90 @@ emit_visualizer_events: u32, has_pre_crash_handler: bool, pub const internal_prefix = "/_bun"; -pub const client_prefix = internal_prefix ++ "/client"; +/// Assets which are routed to the `Assets` storage. pub const asset_prefix = internal_prefix ++ "/asset"; -pub const css_prefix = internal_prefix ++ "/css"; +/// Client scripts are available at `/_bun/client/{name}-{rbi}{generation}.js` +/// where: +/// - `name` is the display name of the route, such as "index" or +/// "about". It is ignored when routing. +/// - `rbi` is the route bundle index, in padded hex (e.g. `00000001`) +/// - `generation` which is initialized to a random value. This value is +/// re-randomized whenever `client_bundle` is invalidated. +/// +/// Example: `/_bun/client/index-00000000f209a20e.js` +pub const client_prefix = internal_prefix ++ "/client"; pub const RouteBundle = struct { pub const Index = bun.GenericIndex(u30, RouteBundle); - route: Route.Index, - server_state: State, - - /// Used to communicate over WebSocket the pattern. The HMR client contains code - /// to match this against the URL bar to determine if a reloaded route applies. - full_pattern: []const u8, - /// Generated lazily when the client JS is requested (HTTP GET /_bun/client/*.js), - /// which is only needed when a hard-reload is performed. - /// - /// Freed when a client module updates. - client_bundle: ?[]const u8, - /// Contain the list of serialized failures. Hashmap allows for - /// efficient lookup and removal of failing files. - /// When state == .evaluation_failure, this is popualted with that error. - evaluate_failure: ?SerializedFailure, - - // TODO: micro-opt: use a singular strong - - /// Cached to avoid re-creating the array every request. - /// Invalidated when a layout is added or removed from this route. - cached_module_list: JSC.Strong, - /// Cached to avoid re-creating the string every request. - /// Invalidated when any client file associated with the route is updated. - cached_client_bundle_url: JSC.Strong, - /// Cached to avoid re-creating the array every request. - /// Invalidated when the list of CSS files changes. - cached_css_file_array: JSC.Strong, + /// There are two distinct types of route bundles. + data: union(enum) { + /// FrameworkRouter provided route + framework: Framework, + /// HTMLBundle provided route + html: HTML, + }, + /// Generated lazily when the client JS is requested. + /// Invalidated when a downstream client module updates. + client_bundle: ?*StaticRoute, + + /// If the client tries to load a script with the wrong generation, it will + /// receive a bundle that instantly reloads the page, implying a bundle + /// change has occurred while fetching the script. + client_script_generation: u32, /// Reference count of how many HmrSockets say they are on this route. This /// allows hot-reloading events to reduce the amount of times it traces the /// graph. - active_viewers: usize, + active_viewers: u32, + + pub const Framework = struct { + route_index: Route.Index, + + // TODO: micro-opt: use a singular strong + + /// Cached to avoid re-creating the array every request. + /// TODO: Invalidated when a layout is added or removed from this route. + cached_module_list: JSC.Strong, + /// Cached to avoid re-creating the string every request. + /// TODO: Invalidated when any client file associated with the route is updated. + cached_client_bundle_url: JSC.Strong, + /// Cached to avoid re-creating the array every request. + /// Invalidated when the list of CSS files changes. + cached_css_file_array: JSC.Strong, + + /// Contain the list of serialized failures. Hashmap allows for + /// efficient lookup and removal of failing files. + /// When state == .evaluation_failure, this is populated with that error. + evaluate_failure: ?SerializedFailure, + }; + + pub const HTML = struct { + /// DevServer increments the ref count of this bundle + html_bundle: *HTMLBundle.HTMLBundleRoute, + bundled_file: IncrementalGraph(.client).FileIndex, + /// Invalidated when the HTML file is modified, but not it's imports. + /// The style tag is injected here. + head_end_tag_index: ByteOffset.Optional, + /// Invalidated when the HTML file is modified, but not it's imports. + /// The script tag is injected here. + body_end_tag_index: ByteOffset.Optional, + /// The HTML file bundled, from the bundler. + bundled_html_text: ?[]const u8, + /// Derived from `bundled_html_text` + `client_script_generation` + /// and css information. Invalidated when: + /// - The HTML file itself modified. + /// - The list of CSS files changes. + /// - TODO: Any downstream file is rebundled. + cached_response: ?*StaticRoute, + + const ByteOffset = bun.GenericIndex(u32, u8); + }; /// A union is not used so that `bundler_failure_logs` can re-use memory, as /// this state frequently changes between `loaded` and the failure variants. - const State = enum { + pub const State = enum { /// In development mode, routes are lazily built. This state implies a /// build of this route has never been run. It is possible to bundle the /// route entry point and still have an unqueued route if another route @@ -207,6 +275,45 @@ pub const RouteBundle = struct { /// at fault of bundling, nor would re-bundling change anything. loaded, }; + + pub const UnresolvedIndex = union(enum) { + /// FrameworkRouter provides a fullstack server-side route + framework: FrameworkRouter.Route.Index, + /// HTMLBundle provides a frontend-only route, SPA-style + html: *HTMLBundle.HTMLBundleRoute, + }; + + pub fn invalidateClientBundle(self: *RouteBundle) void { + if (self.client_bundle) |bundle| { + bundle.deref(); + self.client_bundle = null; + } + self.client_script_generation = std.crypto.random.int(u32); + switch (self.data) { + .framework => |*fw| fw.cached_client_bundle_url.clear(), + .html => |*html| if (html.cached_response) |cached_response| { + cached_response.deref(); + html.cached_response = null; + }, + } + } + + /// Does NOT count @sizeOf(RouteBundle) + pub fn memoryCost(self: *const RouteBundle) usize { + var cost: usize = 0; + if (self.client_bundle) |bundle| cost += bundle.memoryCost(); + switch (self.data) { + .framework => { + // the JSC.Strong children do not support memoryCost. likely not needed + // .evaluate_failure is not owned + }, + .html => |*html| { + if (html.bundled_html_text) |text| cost += text.len; + if (html.cached_response) |cached_response| cost += cached_response.memoryCost(); + }, + } + return cost; + } }; /// DevServer is stored on the heap, storing its allocator. @@ -233,11 +340,11 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { .root = options.root, .vm = options.vm, .server = null, - .directory_watchers = DirectoryWatchStore.empty, + .directory_watchers = .empty, .server_fetch_function_callback = .{}, .server_register_update_callback = .{}, .generation = 0, - .graph_safety_lock = bun.DebugThreadLock.unlocked, + .graph_safety_lock = .unlocked, .dump_dir = dump_dir, .framework = options.framework, .bundler_options = options.bundler_options, @@ -245,28 +352,32 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { .has_pre_crash_handler = bun.FeatureFlags.bake_debugging_features and options.dump_state_on_crash orelse bun.getRuntimeFeatureFlag("BUN_DUMP_STATE_ON_CRASH"), - .css_files = .{}, - .route_js_payloads = .{}, - // .assets = .{}, - - .client_graph = IncrementalGraph(.client).empty, - .server_graph = IncrementalGraph(.server).empty, - .incremental_result = IncrementalResult.empty, - .route_lookup = .{}, - .route_bundles = .{}, + .frontend_only = options.frontend_only, + .client_graph = .empty, + .server_graph = .empty, + .incremental_result = .empty, + .route_lookup = .empty, + .route_bundles = .empty, + .html_router = .empty, .current_bundle = null, - .current_bundle_requests = .{}, .next_bundle = .{ - .route_queue = .{}, + .route_queue = .empty, .reload_event = null, .requests = .{}, }, - - .log = bun.logger.Log.init(allocator), - - .server_bundler = undefined, - .client_bundler = undefined, - .ssr_bundler = undefined, + .assets = .{ + .path_map = .empty, + .files = .empty, + .refs = .empty, + }, + .log = .init(allocator), + .plugin_state = .unknown, + .bundling_failures = .{}, + .deferred_request_pool = .init(allocator), + + .server_transpiler = undefined, + .client_transpiler = undefined, + .ssr_transpiler = undefined, .bun_watcher = undefined, .configuration_hash_key = undefined, .router = undefined, @@ -293,25 +404,25 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { dev.bun_watcher.start() catch |err| return global.throwError(err, "while initializing file watcher thread for development server"); - dev.server_bundler.resolver.watcher = dev.bun_watcher.getResolveWatcher(); - dev.client_bundler.resolver.watcher = dev.bun_watcher.getResolveWatcher(); - dev.ssr_bundler.resolver.watcher = dev.bun_watcher.getResolveWatcher(); + dev.server_transpiler.resolver.watcher = dev.bun_watcher.getResolveWatcher(); + dev.client_transpiler.resolver.watcher = dev.bun_watcher.getResolveWatcher(); + dev.ssr_transpiler.resolver.watcher = dev.bun_watcher.getResolveWatcher(); dev.watcher_atomics = WatcherAtomics.init(dev); - dev.framework.initBundler(allocator, &dev.log, .development, .server, &dev.server_bundler) catch |err| + dev.framework.initBundler(allocator, &dev.log, .development, .server, &dev.server_transpiler) catch |err| return global.throwError(err, generic_action); - dev.client_bundler.options.dev_server = dev; - dev.framework.initBundler(allocator, &dev.log, .development, .client, &dev.client_bundler) catch |err| + dev.server_transpiler.options.dev_server = dev; + dev.framework.initBundler(allocator, &dev.log, .development, .client, &dev.client_transpiler) catch |err| return global.throwError(err, generic_action); - dev.server_bundler.options.dev_server = dev; + dev.client_transpiler.options.dev_server = dev; if (separate_ssr_graph) { - dev.framework.initBundler(allocator, &dev.log, .development, .ssr, &dev.ssr_bundler) catch |err| + dev.framework.initBundler(allocator, &dev.log, .development, .ssr, &dev.ssr_transpiler) catch |err| return global.throwError(err, generic_action); - dev.ssr_bundler.options.dev_server = dev; + dev.ssr_transpiler.options.dev_server = dev; } - dev.framework = dev.framework.resolve(&dev.server_bundler.resolver, &dev.client_bundler.resolver, options.arena) catch { + dev.framework = dev.framework.resolve(&dev.server_transpiler.resolver, &dev.client_transpiler.resolver, options.arena) catch { if (dev.framework.is_built_in_react) try bake.Framework.addReactInstallCommandNote(&dev.log); return global.throwValue(dev.log.toJSAggregateError(global, bun.String.static("Framework is missing required files!"))); @@ -329,8 +440,8 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { Output.panic("unhandled {}", .{e})).unwrap() catch |e| Output.panic("unhandled {}", .{e}); bun.writeAnyToHasher(&hash, stat.mtime()); - hash.update(bake.getHmrRuntime(.client)); - hash.update(bake.getHmrRuntime(.server)); + hash.update(bake.getHmrRuntime(.client).code); + hash.update(bake.getHmrRuntime(.server).code); } else { hash.update(bun.Environment.git_sha_short); } @@ -391,6 +502,8 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { } hash.update(&.{0}); + bun.writeAnyToHasher(&hash, options.frontend_only); + break :hash_key std.fmt.bytesToHex(std.mem.asBytes(&hash.final()), .lower); }; @@ -400,7 +513,9 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { assert(try dev.client_graph.insertStale(rfr.import_source, false) == IncrementalGraph(.client).react_refresh_index); } - dev.initServerRuntime(); + if (!options.frontend_only) { + dev.initServerRuntime(); + } // Initialize FrameworkRouter dev.router = router: { @@ -409,7 +524,7 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { for (options.framework.file_system_router_types, 0..) |fsr, i| { const joined_root = bun.path.joinAbs(dev.root, .auto, fsr.root); - const entry = dev.server_bundler.resolver.readDirInfoIgnoreError(joined_root) orelse + const entry = dev.server_transpiler.resolver.readDirInfoIgnoreError(joined_root) orelse continue; const server_file = try dev.server_graph.insertStaleExtra(fsr.entry_server, false, true); @@ -439,10 +554,12 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { break :router try FrameworkRouter.initEmpty(dev.root, types.items, allocator); }; - // TODO: move scanning to be one tick after server startup. this way the - // line saying the server is ready shows quicker, and route errors show up - // after that line. - try dev.scanInitialRoutes(); + if (options.frontend_only) { + // TODO: move scanning to be one tick after server startup. this way the + // line saying the server is ready shows quicker, and route errors show up + // after that line. + try dev.scanInitialRoutes(); + } if (bun.FeatureFlags.bake_debugging_features and dev.has_pre_crash_handler) try bun.crash_handler.appendPreCrashHandler(DevServer, dev, dumpStateDueToCrash); @@ -450,8 +567,242 @@ pub fn init(options: Options) bun.JSOOM!*DevServer { return dev; } +pub fn deinit(dev: *DevServer) void { + // TODO: Currently deinit is not implemented, as it was assumed to be alive for + // the remainder of this process' lifespan. This isn't always true. + const allocator = dev.allocator; + + // _ = VoidFieldTypes(DevServer){ + // // has no action taken + // .allocator = {}, + // .configuration_hash_key = {}, + // .graph_safety_lock = {}, + // .bun_watcher = {}, + // .watcher_atomics = {}, + // .plugin_state = {}, + // .generation = {}, + // .bundles_since_last_error = {}, + // .emit_visualizer_events = {}, + // .dump_dir = {}, + // .frontend_only = {}, + // .server_fetch_function_callback = {}, + // .server_register_update_callback = {}, + // .deferred_request_pool = {}, + + // .has_pre_crash_handler = if (dev.has_pre_crash_handler) + // bun.crash_handler.removePreCrashHandler(dev), + + // // pointers that are not considered a part of DevServer + // .vm = {}, + // .server = {}, + // .server_transpiler = {}, + // .client_transpiler = {}, + // .ssr_transpiler = {}, + // .log = {}, + // .framework = {}, // TODO: maybe + // .bundler_options = {}, // TODO: maybe + + // // to be counted. + // .root = { + // cost += dev.root.len; + // }, + // .router = { + // cost += dev.router.memoryCost(); + // }, + // .route_bundles = for (dev.route_bundles.items) |*bundle| { + // cost += bundle.memoryCost(); + // }, + // .server_graph = { + // cost += dev.server_graph.memoryCost(); + // }, + // .client_graph = { + // cost += dev.client_graph.memoryCost(); + // }, + // .assets = { + // cost += dev.assets.memoryCost(); + // }, + // .incremental_result = { + // cost += memoryCostArrayList(dev.incremental_result.client_components_added); + // cost += memoryCostArrayList(dev.incremental_result.html_routes_affected); + // cost += memoryCostArrayList(dev.incremental_result.framework_routes_affected); + // cost += memoryCostArrayList(dev.incremental_result.client_components_removed); + // cost += memoryCostArrayList(dev.incremental_result.failures_removed); + // cost += memoryCostArrayList(dev.incremental_result.client_components_affected); + // cost += memoryCostArrayList(dev.incremental_result.failures_added); + // }, + // .has_tailwind_plugin_hack = if (dev.has_tailwind_plugin_hack) |hack| { + // cost += memoryCostArrayHashMap(hack); + // }, + // .directory_watchers = { + // cost += memoryCostArrayList(dev.directory_watchers.dependencies); + // cost += memoryCostArrayList(dev.directory_watchers.dependencies_free_list); + // cost += memoryCostArrayHashMap(dev.directory_watchers.watches); + // for (dev.directory_watchers.dependencies.items) |dep| { + // cost += dep.specifier.len; + // } + // }, + // .html_router = { + // // std does not provide a way to measure exact allocation size of HashMapUnmanaged + // cost += dev.html_router.map.capacity() * (@sizeOf(*HTMLBundle.HTMLBundleRoute) + @sizeOf([]const u8)); + // // DevServer does not count the referenced HTMLBundle.HTMLBundleRoutes + // }, + // .bundling_failures = { + // cost += memoryCostSlice(dev.bundling_failures.keys()); + // for (dev.bundling_failures.keys()) |failure| { + // cost += failure.data.len; + // } + // }, + // .current_bundle = { + // // All entries are owned by the bundler arena, not DevServer, except for `requests` + // if (dev.current_bundle) |bundle| { + // var r = bundle.requests.first; + // while (r) |request| : (r = request.next) { + // cost += @sizeOf(DeferredRequest.Node); + // } + // } + // }, + // .next_bundle = { + // var r = dev.next_bundle.requests.first; + // while (r) |request| : (r = request.next) { + // cost += @sizeOf(DeferredRequest.Node); + // } + // cost += memoryCostArrayHashMap(dev.next_bundle.route_queue); + // }, + // .route_lookup = { + // cost += memoryCostArrayHashMap(dev.route_lookup); + // }, + // }; + + allocator.destroy(dev); + // if (bun.Environment.isDebug) + // bun.todoPanic(@src(), "bake.DevServer.deinit()", .{}); +} + +/// Returns an estimation for how many bytes DevServer is explicitly aware of. +/// If this number stays constant but RSS grows, then there is a memory leak. If +/// this number grows out of control, then incremental garbage collection is not +/// good enough. +/// +/// Memory measurements are important as DevServer has a long lifetime, but +/// unlike the HTTP server, it controls a lot of objects that are frequently +/// being added, removed, and changed (as the developer edits source files). It +/// is exponentially easy to mess up memory management. +pub fn memoryCost(dev: *DevServer) usize { + var cost: usize = @sizeOf(DevServer); + // See https://github.com/ziglang/zig/issues/21879 + _ = VoidFieldTypes(DevServer){ + // does not contain pointers + .allocator = {}, + .configuration_hash_key = {}, + .graph_safety_lock = {}, + .bun_watcher = {}, + .watcher_atomics = {}, + .plugin_state = {}, + .generation = {}, + .bundles_since_last_error = {}, + .emit_visualizer_events = {}, + .dump_dir = {}, + .has_pre_crash_handler = {}, + .frontend_only = {}, + .server_fetch_function_callback = {}, + .server_register_update_callback = {}, + .deferred_request_pool = {}, + + // pointers that are not considered a part of DevServer + .vm = {}, + .server = {}, + .server_transpiler = {}, + .client_transpiler = {}, + .ssr_transpiler = {}, + .log = {}, + .framework = {}, // TODO: maybe + .bundler_options = {}, // TODO: maybe + + // to be counted. + .root = { + cost += dev.root.len; + }, + .router = { + cost += dev.router.memoryCost(); + }, + .route_bundles = for (dev.route_bundles.items) |*bundle| { + cost += bundle.memoryCost(); + }, + .server_graph = { + cost += dev.server_graph.memoryCost(); + }, + .client_graph = { + cost += dev.client_graph.memoryCost(); + }, + .assets = { + cost += dev.assets.memoryCost(); + }, + .incremental_result = { + cost += memoryCostArrayList(dev.incremental_result.client_components_added); + cost += memoryCostArrayList(dev.incremental_result.html_routes_affected); + cost += memoryCostArrayList(dev.incremental_result.framework_routes_affected); + cost += memoryCostArrayList(dev.incremental_result.client_components_removed); + cost += memoryCostArrayList(dev.incremental_result.failures_removed); + cost += memoryCostArrayList(dev.incremental_result.client_components_affected); + cost += memoryCostArrayList(dev.incremental_result.failures_added); + }, + .has_tailwind_plugin_hack = if (dev.has_tailwind_plugin_hack) |hack| { + cost += memoryCostArrayHashMap(hack); + }, + .directory_watchers = { + cost += memoryCostArrayList(dev.directory_watchers.dependencies); + cost += memoryCostArrayList(dev.directory_watchers.dependencies_free_list); + cost += memoryCostArrayHashMap(dev.directory_watchers.watches); + for (dev.directory_watchers.dependencies.items) |dep| { + cost += dep.specifier.len; + } + }, + .html_router = { + // std does not provide a way to measure exact allocation size of HashMapUnmanaged + cost += dev.html_router.map.capacity() * (@sizeOf(*HTMLBundle.HTMLBundleRoute) + @sizeOf([]const u8)); + // DevServer does not count the referenced HTMLBundle.HTMLBundleRoutes + }, + .bundling_failures = { + cost += memoryCostSlice(dev.bundling_failures.keys()); + for (dev.bundling_failures.keys()) |failure| { + cost += failure.data.len; + } + }, + .current_bundle = { + // All entries are owned by the bundler arena, not DevServer, except for `requests` + if (dev.current_bundle) |bundle| { + var r = bundle.requests.first; + while (r) |request| : (r = request.next) { + cost += @sizeOf(DeferredRequest.Node); + } + } + }, + .next_bundle = { + var r = dev.next_bundle.requests.first; + while (r) |request| : (r = request.next) { + cost += @sizeOf(DeferredRequest.Node); + } + cost += memoryCostArrayHashMap(dev.next_bundle.route_queue); + }, + .route_lookup = { + cost += memoryCostArrayHashMap(dev.route_lookup); + }, + }; + return cost; +} + +fn memoryCostArrayList(slice: anytype) usize { + return slice.capacity * @sizeOf(@typeInfo(@TypeOf(slice.items)).pointer.child); +} +fn memoryCostSlice(slice: anytype) usize { + return slice.len * @sizeOf(@typeInfo(@TypeOf(slice)).pointer.child); +} +fn memoryCostArrayHashMap(map: anytype) usize { + return @TypeOf(map.entries).capacityInBytes(map.entries.capacity); +} + fn initServerRuntime(dev: *DevServer) void { - const runtime = bun.String.static(bun.bake.getHmrRuntime(.server)); + const runtime = bun.String.static(bun.bake.getHmrRuntime(.server).code); const interface = c.BakeLoadInitialServerCode( @ptrCast(dev.vm.global), @@ -479,7 +830,7 @@ fn initServerRuntime(dev: *DevServer) void { fn scanInitialRoutes(dev: *DevServer) !void { try dev.router.scanAll( dev.allocator, - &dev.server_bundler.resolver, + &dev.server_transpiler.resolver, FrameworkRouter.InsertionContext.wrap(DevServer, dev), ); @@ -487,94 +838,86 @@ fn scanInitialRoutes(dev: *DevServer) !void { try dev.client_graph.ensureStaleBitCapacity(true); } -pub fn attachRoutes(dev: *DevServer, server: anytype) !void { +/// Returns true if a catch-all handler was attached. +pub fn attachRoutes(dev: *DevServer, server: anytype) !bool { dev.server = bun.JSC.API.AnyServer.from(server); const app = server.app.?; + const is_ssl = @typeInfo(@TypeOf(app)).pointer.child.is_ssl; - // For this to work, the route handlers need to be augmented to use the comptime - // SSL parameter. It's worth considering removing the SSL boolean. - if (@TypeOf(app) == *uws.NewApp(true)) { - bun.todoPanic(@src(), "DevServer does not support SSL yet", .{}); - } - - app.get(client_prefix ++ "/:route", *DevServer, dev, onJsRequest); - app.get(asset_prefix ++ "/:asset", *DevServer, dev, onAssetRequest); - app.get(css_prefix ++ "/:asset", *DevServer, dev, onCssRequest); - app.get(internal_prefix ++ "/src/*", *DevServer, dev, onSrcRequest); + app.get(client_prefix ++ "/:route", *DevServer, dev, wrapGenericRequestHandler(onJsRequest, is_ssl)); + app.get(asset_prefix ++ "/:asset", *DevServer, dev, wrapGenericRequestHandler(onAssetRequest, is_ssl)); + app.get(internal_prefix ++ "/src/*", *DevServer, dev, wrapGenericRequestHandler(onSrcRequest, is_ssl)); app.ws( internal_prefix ++ "/hmr", dev, 0, - uws.WebSocketBehavior.Wrap(DevServer, HmrSocket, false).apply(.{}), + uws.WebSocketBehavior.Wrap(DevServer, HmrSocket, is_ssl).apply(.{}), ); - if (bun.FeatureFlags.bake_debugging_features) - app.get(internal_prefix ++ "/incremental_visualizer", *DevServer, dev, onIncrementalVisualizer); + if (bun.FeatureFlags.bake_debugging_features) { + app.get( + internal_prefix ++ "/incremental_visualizer", + *DevServer, + dev, + wrapGenericRequestHandler(onIncrementalVisualizer, is_ssl), + ); + } - app.any("/*", *DevServer, dev, onRequest); + // Only attach a catch-all handler if the framework has filesystem router + // types. Otherwise, this can just be Bun.serve's default handler. + if (dev.framework.file_system_router_types.len > 0) { + app.any("/*", *DevServer, dev, wrapGenericRequestHandler(onRequest, is_ssl)); + return true; + } else { + return false; + } } -pub fn deinit(dev: *DevServer) void { - // TODO: Currently deinit is not implemented, as it was assumed to be alive for - // the remainder of this process' lifespan. This isn't always true. - const allocator = dev.allocator; - if (dev.has_pre_crash_handler) - bun.crash_handler.removePreCrashHandler(dev); - allocator.destroy(dev); - // if (bun.Environment.isDebug) - // bun.todoPanic(@src(), "bake.DevServer.deinit()", .{}); -} +fn onJsRequest(dev: *DevServer, req: *Request, resp: AnyResponse) void { + const route_id = req.parameter(0); + if (!bun.strings.hasSuffixComptime(route_id, ".js")) + return req.setYield(true); + const min_len = "-00000000FFFFFFFF.js".len; + if (route_id.len < min_len) + return req.setYield(true); + const hex = route_id[route_id.len - min_len + 1 ..][0 .. @sizeOf(u64) * 2]; + if (hex.len != @sizeOf(u64) * 2) + return req.setYield(true); + const id = parseHexToInt(u64, hex) orelse + return req.setYield(true); -fn onJsRequest(dev: *DevServer, req: *Request, resp: *Response) void { - const maybe_route = route: { - const route_id = req.parameter(0); - if (!bun.strings.hasSuffixComptime(route_id, ".js")) - return req.setYield(true); - if (!bun.strings.hasPrefixComptime(route_id, "route.")) - return req.setYield(true); - const i = parseHexToInt(u64, route_id["route.".len .. route_id.len - ".js".len]) orelse - return req.setYield(true); - break :route dev.route_js_payloads.get(i) orelse - return req.setYield(true); - }; + const route_bundle_index: RouteBundle.Index = .init(@intCast(id & 0xFFFFFFFF)); + const generation: u32 = @intCast(id >> 32); - if (maybe_route.unwrap()) |route| { - dev.ensureRouteIsBundled(route, .js_payload, req, resp) catch bun.outOfMemory(); - } else { - @panic("TODO: generate client bundle with no source files"); - } -} + if (route_bundle_index.get() >= dev.route_bundles.items.len) + return req.setYield(true); -fn onAssetRequest(dev: *DevServer, req: *Request, resp: *Response) void { - _ = dev; - _ = req; - _ = resp; - bun.todoPanic(@src(), "serve asset file", .{}); - // const route_id = req.parameter(0); - // const asset = dev.assets.get(route_id) orelse - // return req.setYield(true); - // _ = asset; // autofix + const route_bundle = dev.route_bundles.items[route_bundle_index.get()]; + if (route_bundle.client_script_generation != generation or + route_bundle.server_state != .loaded) + { + bun.Output.debugWarn("TODO: Outdated JS Payload", .{}); + return req.setYield(true); + } + dev.onJsRequestWithBundle(route_bundle_index, resp, bun.http.Method.which(req.method()) orelse .POST); } -fn onCssRequest(dev: *DevServer, req: *Request, resp: *Response) void { +fn onAssetRequest(dev: *DevServer, req: *Request, resp: AnyResponse) void { const param = req.parameter(0); - if (!bun.strings.hasSuffixComptime(param, ".css")) - return req.setYield(true); - const hex = param[0 .. param.len - ".css".len]; - if (hex.len != @sizeOf(u64) * 2) + if (param.len < @sizeOf(u64) * 2) return req.setYield(true); - + const hex = param[0 .. @sizeOf(u64) * 2]; var out: [@sizeOf(u64)]u8 = undefined; assert((std.fmt.hexToBytes(&out, hex) catch return req.setYield(true)).len == @sizeOf(u64)); const hash: u64 = @bitCast(out); - - const css = dev.css_files.get(hash) orelse + debug.log("onAssetRequest {} {s}", .{ hash, param }); + const asset = dev.assets.get(hash) orelse return req.setYield(true); - - sendTextFile(css, MimeType.css.value, resp); + req.setYield(false); + asset.on(resp); } fn parseHexToInt(comptime T: type, slice: []const u8) ?T { @@ -583,11 +926,29 @@ fn parseHexToInt(comptime T: type, slice: []const u8) ?T { return @bitCast(out); } -fn onIncrementalVisualizer(_: *DevServer, _: *Request, resp: *Response) void { +inline fn wrapGenericRequestHandler( + comptime handler: anytype, + comptime is_ssl: bool, +) fn ( + dev: *DevServer, + req: *Request, + resp: *uws.NewApp(is_ssl).Response, +) void { + const fn_info = @typeInfo(@TypeOf(handler)).@"fn"; + assert(fn_info.params.len == 3); + const uses_any_response = if (fn_info.params[2].type) |t| t == AnyResponse else false; + return struct { + fn handle(dev: *DevServer, req: *Request, resp: *uws.NewApp(is_ssl).Response) void { + handler(dev, req, if (uses_any_response) AnyResponse.init(resp) else resp); + } + }.handle; +} + +fn onIncrementalVisualizer(_: *DevServer, _: *Request, resp: anytype) void { resp.corked(onIncrementalVisualizerCorked, .{resp}); } -fn onIncrementalVisualizerCorked(resp: *Response) void { +fn onIncrementalVisualizerCorked(resp: anytype) void { const code = if (Environment.codegen_embed) @embedFile("incremental_visualizer.html") else @@ -598,159 +959,210 @@ fn onIncrementalVisualizerCorked(resp: *Response) void { fn ensureRouteIsBundled( dev: *DevServer, - route_index: Route.Index, - kind: DeferredRequest.Data.Tag, + route_bundle_index: RouteBundle.Index, + kind: DeferredRequest.Handler.Kind, req: *Request, - resp: *Response, + resp: AnyResponse, ) bun.OOM!void { - const route_bundle_index = try dev.getOrPutRouteBundle(route_index); - - // TODO: Zig 0.14 gets labelled continue: - // - Remove the `while` - // - Move the code after this switch into `.loaded =>` - // - Replace `break` with `continue :sw .loaded` - // - Replace `continue` with `continue :sw ` - while (true) { - switch (dev.routeBundlePtr(route_bundle_index).server_state) { - .unqueued => { - try dev.next_bundle.requests.ensureUnusedCapacity(dev.allocator, 1); - if (dev.current_bundle != null) { - try dev.next_bundle.route_queue.ensureUnusedCapacity(dev.allocator, 1); - } - - const deferred: DeferredRequest = .{ - .route_bundle_index = route_bundle_index, - .data = switch (kind) { - .js_payload => .{ .js_payload = resp }, - .server_handler => .{ - .server_handler = (dev.server.?.DebugHTTPServer.prepareJsRequestContext(req, resp, null) orelse return) - .save(dev.vm.global, req, resp), - }, + assert(dev.server != null); + sw: switch (dev.routeBundlePtr(route_bundle_index).server_state) { + .unqueued => { + if (dev.current_bundle != null) { + try dev.next_bundle.route_queue.put(dev.allocator, route_bundle_index, {}); + dev.routeBundlePtr(route_bundle_index).server_state = .bundling; + try dev.deferRequest(&dev.next_bundle.requests, route_bundle_index, kind, req, resp); + } else { + // If plugins are not yet loaded, prepare them. + // In the case plugins are set to &.{}, this will not hit `.pending`. + plugin: switch (dev.plugin_state) { + .unknown => if (dev.bundler_options.plugin != null) { + // Framework-provided plugin is likely going to be phased out later + dev.plugin_state = .loaded; + } else { + // TODO: implement a proper solution here + dev.has_tailwind_plugin_hack = if (dev.vm.transpiler.options.serve_plugins) |serve_plugins| + for (serve_plugins) |plugin| { + if (bun.strings.includes(plugin, "tailwind")) break .empty; + } else null + else + null; + + switch (dev.server.?.getOrLoadPlugins(.{ .dev_server = dev })) { + .pending => { + dev.plugin_state = .pending; + continue :plugin .pending; + }, + .err => { + dev.plugin_state = .err; + continue :plugin .err; + }, + .ready => |ready| { + dev.plugin_state = .loaded; + dev.bundler_options.plugin = ready; + }, + } }, - }; - errdefer @compileError("cannot error since the request is already stored"); - - dev.next_bundle.requests.appendAssumeCapacity(deferred); - if (dev.current_bundle != null) { - dev.next_bundle.route_queue.putAssumeCapacity(route_bundle_index, {}); - } else { - var sfa = std.heap.stackFallback(4096, dev.allocator); - const temp_alloc = sfa.get(); - - var entry_points: EntryPointList = EntryPointList.empty; - defer entry_points.deinit(temp_alloc); - - dev.appendRouteEntryPointsIfNotStale(&entry_points, temp_alloc, route_index) catch bun.outOfMemory(); + .pending => { + try dev.next_bundle.route_queue.put(dev.allocator, route_bundle_index, {}); + dev.routeBundlePtr(route_bundle_index).server_state = .bundling; + try dev.deferRequest(&dev.next_bundle.requests, route_bundle_index, kind, req, resp); + return; + }, + .err => { + // TODO: render plugin error page + resp.endWithoutBody(true); + return; + }, + .loaded => {}, + } - if (entry_points.set.count() == 0) { - if (dev.bundling_failures.count() > 0) { - dev.routeBundlePtr(route_bundle_index).server_state = .possible_bundling_failures; - } else { - dev.routeBundlePtr(route_bundle_index).server_state = .loaded; - } - continue; + // Prepare a bundle with just this route. + var sfa = std.heap.stackFallback(4096, dev.allocator); + const temp_alloc = sfa.get(); + + var entry_points: EntryPointList = .empty; + defer entry_points.deinit(temp_alloc); + try dev.appendRouteEntryPointsIfNotStale(&entry_points, temp_alloc, route_bundle_index); + + // If all files were already bundled (possible with layouts), + // then no entry points will be queued up here. That does + // not mean the route is ready for presentation. + if (entry_points.set.count() == 0) { + if (dev.bundling_failures.count() > 0) { + dev.routeBundlePtr(route_bundle_index).server_state = .possible_bundling_failures; + continue :sw .possible_bundling_failures; + } else { + dev.routeBundlePtr(route_bundle_index).server_state = .loaded; + continue :sw .loaded; } + } - dev.startAsyncBundle( - entry_points, - false, - std.time.Timer.start() catch @panic("timers unsupported"), - ) catch |err| { - if (dev.log.hasAny()) { - dev.log.print(Output.errorWriterBuffered()) catch {}; - Output.flush(); - } - Output.panic("Fatal error while initializing bundle job: {}", .{err}); - }; + try dev.deferRequest(&dev.next_bundle.requests, route_bundle_index, kind, req, resp); - dev.routeBundlePtr(route_bundle_index).server_state = .bundling; - } - return; - }, - .bundling => { - bun.assert(dev.current_bundle != null); - try dev.current_bundle_requests.ensureUnusedCapacity(dev.allocator, 1); - - const deferred: DeferredRequest = .{ - .route_bundle_index = route_bundle_index, - .data = switch (kind) { - .js_payload => .{ .js_payload = resp }, - .server_handler => .{ - .server_handler = (dev.server.?.DebugHTTPServer.prepareJsRequestContext(req, resp, null) orelse return) - .save(dev.vm.global, req, resp), - }, - }, - }; + dev.startAsyncBundle( + entry_points, + false, + std.time.Timer.start() catch @panic("timers unsupported"), + ) catch bun.outOfMemory(); + } - dev.current_bundle_requests.appendAssumeCapacity(deferred); - return; - }, - .possible_bundling_failures => { - // TODO: perform a graph trace to find just the errors that are needed - if (dev.bundling_failures.count() > 0) { - resp.corked(sendSerializedFailures, .{ - dev, - resp, - dev.bundling_failures.keys(), - .bundler, - }); - return; - } else { - dev.routeBundlePtr(route_bundle_index).server_state = .loaded; - break; + dev.routeBundlePtr(route_bundle_index).server_state = .bundling; + }, + .bundling => { + bun.assert(dev.current_bundle != null); + try dev.deferRequest(&dev.current_bundle.?.requests, route_bundle_index, kind, req, resp); + }, + .possible_bundling_failures => { + if (dev.bundling_failures.count() > 0) { + // Trace the graph to see if there are any failures that are + // reachable by this route. + switch (try checkRouteFailures(dev, route_bundle_index, resp)) { + .stop => return, + .ok => {}, // Errors were cleared or not in the way. } - }, - .evaluation_failure => { - resp.corked(sendSerializedFailures, .{ - dev, - resp, - (&(dev.routeBundlePtr(route_bundle_index).evaluate_failure orelse @panic("missing error")))[0..1], - .evaluation, - }); - return; - }, - .loaded => break, - } + } - // this error is here to make sure there are no accidental loop exits - @compileError("all branches above should `return`, `break` or `continue`"); + dev.routeBundlePtr(route_bundle_index).server_state = .loaded; + continue :sw .loaded; + }, + .evaluation_failure => { + resp.corked(sendSerializedFailures, .{ + dev, + resp, + (&(dev.routeBundlePtr(route_bundle_index).data.framework.evaluate_failure.?))[0..1], + .evaluation, + }); + }, + .loaded => switch (kind) { + .server_handler => dev.onFrameworkRequestWithBundle(route_bundle_index, .{ .stack = req }, resp), + .bundled_html_page => dev.onHtmlRequestWithBundle(route_bundle_index, resp, bun.http.Method.which(req.method()) orelse .POST), + }, } +} + +fn deferRequest( + dev: *DevServer, + requests_array: *DeferredRequest.List, + route_bundle_index: RouteBundle.Index, + kind: DeferredRequest.Handler.Kind, + req: *Request, + resp: AnyResponse, +) !void { + const deferred = dev.deferred_request_pool.get(); + deferred.data = .{ + .route_bundle_index = route_bundle_index, + .handler = switch (kind) { + .bundled_html_page => .{ .bundled_html_page = .{ .response = resp, .method = bun.http.Method.which(req.method()) orelse .POST } }, + .server_handler => .{ + .server_handler = dev.server.?.prepareAndSaveJsRequestContext(req, resp, dev.vm.global) orelse return, + }, + }, + }; + resp.onAborted(*DeferredRequest, DeferredRequest.onAbort, &deferred.data); + requests_array.prepend(deferred); +} - switch (kind) { - .server_handler => dev.onRequestWithBundle(route_bundle_index, .{ .stack = req }, resp), - .js_payload => dev.onJsRequestWithBundle(route_bundle_index, resp), +fn checkRouteFailures(dev: *DevServer, route_bundle_index: RouteBundle.Index, resp: anytype) !enum { stop, ok } { + var sfa_state = std.heap.stackFallback(65536, dev.allocator); + const sfa = sfa_state.get(); + var gts = try dev.initGraphTraceState(sfa); + defer gts.deinit(sfa); + defer dev.incremental_result.failures_added.clearRetainingCapacity(); + dev.graph_safety_lock.lock(); + defer dev.graph_safety_lock.unlock(); + try dev.traceAllRouteImports(dev.routeBundlePtr(route_bundle_index), >s, .find_errors); + if (dev.incremental_result.failures_added.items.len > 0) { + resp.corked(sendSerializedFailures, .{ + dev, + resp, + dev.incremental_result.failures_added.items, + .bundler, + }); + return .stop; + } else { + // Failures are unreachable by this route, so it is OK to load. + return .ok; } } -fn appendRouteEntryPointsIfNotStale(dev: *DevServer, entry_points: *EntryPointList, alloc: Allocator, route_index: Route.Index) bun.OOM!void { +fn appendRouteEntryPointsIfNotStale(dev: *DevServer, entry_points: *EntryPointList, alloc: Allocator, rbi: RouteBundle.Index) bun.OOM!void { const server_file_names = dev.server_graph.bundled_files.keys(); const client_file_names = dev.client_graph.bundled_files.keys(); // Build a list of all files that have not yet been bundled. - var route = dev.router.routePtr(route_index); - const router_type = dev.router.typePtr(route.type); - try dev.appendOpaqueEntryPoint(server_file_names, entry_points, alloc, .server, router_type.server_file); - try dev.appendOpaqueEntryPoint(client_file_names, entry_points, alloc, .client, router_type.client_file); - try dev.appendOpaqueEntryPoint(server_file_names, entry_points, alloc, .server, route.file_page); - try dev.appendOpaqueEntryPoint(server_file_names, entry_points, alloc, .server, route.file_layout); - while (route.parent.unwrap()) |parent_index| { - route = dev.router.routePtr(parent_index); - try dev.appendOpaqueEntryPoint(server_file_names, entry_points, alloc, .server, route.file_layout); + switch (dev.routeBundlePtr(rbi).data) { + .framework => |*bundle| { + var route = dev.router.routePtr(bundle.route_index); + const router_type = dev.router.typePtr(route.type); + try dev.appendOpaqueEntryPoint(server_file_names, entry_points, alloc, .server, router_type.server_file); + try dev.appendOpaqueEntryPoint(client_file_names, entry_points, alloc, .client, router_type.client_file); + try dev.appendOpaqueEntryPoint(server_file_names, entry_points, alloc, .server, route.file_page); + try dev.appendOpaqueEntryPoint(server_file_names, entry_points, alloc, .server, route.file_layout); + while (route.parent.unwrap()) |parent_index| { + route = dev.router.routePtr(parent_index); + try dev.appendOpaqueEntryPoint(server_file_names, entry_points, alloc, .server, route.file_layout); + } + }, + .html => |*html| { + try entry_points.append(alloc, html.html_bundle.html_bundle.path, .{ .client = true }); + }, } } -fn onRequestWithBundle( +fn onFrameworkRequestWithBundle( dev: *DevServer, route_bundle_index: RouteBundle.Index, req: bun.JSC.API.SavedRequest.Union, - resp: *Response, + resp: AnyResponse, ) void { - const server_request_callback = dev.server_fetch_function_callback.get() orelse - unreachable; // did not bundle - const route_bundle = dev.routeBundlePtr(route_bundle_index); + assert(route_bundle.data == .framework); + const bundle = &route_bundle.data.framework; + + const server_request_callback = dev.server_fetch_function_callback.get() orelse + unreachable; // did not initialize server code - const router_type = dev.router.typePtr(dev.router.routePtr(route_bundle.route).type); + const router_type = dev.router.typePtr(dev.router.routePtr(bundle.route_index).type); dev.server.?.onRequestFromSaved( req, @@ -766,17 +1178,17 @@ fn onRequestWithBundle( break :str str; }, // routeModules - route_bundle.cached_module_list.get() orelse arr: { + bundle.cached_module_list.get() orelse arr: { const global = dev.vm.global; const keys = dev.server_graph.bundled_files.keys(); var n: usize = 1; - var route = dev.router.routePtr(route_bundle.route); + var route = dev.router.routePtr(bundle.route_index); while (true) { if (route.file_layout != .none) n += 1; route = dev.router.routePtr(route.parent.unwrap() orelse break); } const arr = JSValue.createEmptyArray(global, n); - route = dev.router.routePtr(route_bundle.route); + route = dev.router.routePtr(bundle.route_index); var route_name = bun.String.createUTF8(dev.relativePath(keys[fromOpaqueFileId(.server, route.file_page.unwrap().?).get()])); arr.putIndex(global, 0, route_name.transferToJS(global)); n = 1; @@ -788,85 +1200,276 @@ fn onRequestWithBundle( } route = dev.router.routePtr(route.parent.unwrap() orelse break); } - route_bundle.cached_module_list = JSC.Strong.create(arr, global); + bundle.cached_module_list = JSC.Strong.create(arr, global); break :arr arr; }, // clientId - route_bundle.cached_client_bundle_url.get() orelse str: { - const id, const route_index: Route.Index.Optional = if (router_type.client_file != .none) - .{ std.crypto.random.int(u64), route_bundle.route.toOptional() } - else - // When there is no framework-provided client code, generate - // a JS file so that the hot-reloading code can reload the - // page on server-side changes and show errors in-browser. - .{ 0, .none }; - dev.route_js_payloads.put(dev.allocator, id, route_index) catch bun.outOfMemory(); - const str = bun.String.createFormat(client_prefix ++ "/route.{}.js", .{std.fmt.fmtSliceHexLower(std.mem.asBytes(&id))}) catch bun.outOfMemory(); + bundle.cached_client_bundle_url.get() orelse str: { + const bundle_index: u32 = route_bundle_index.get(); + const generation: u32 = route_bundle.client_script_generation; + const str = bun.String.createFormat(client_prefix ++ "/route-{}{}.js", .{ + std.fmt.fmtSliceHexLower(std.mem.asBytes(&bundle_index)), + std.fmt.fmtSliceHexLower(std.mem.asBytes(&generation)), + }) catch bun.outOfMemory(); defer str.deref(); const js = str.toJS(dev.vm.global); - route_bundle.cached_client_bundle_url = JSC.Strong.create(js, dev.vm.global); + bundle.cached_client_bundle_url = JSC.Strong.create(js, dev.vm.global); break :str js; }, // styles - route_bundle.cached_css_file_array.get() orelse arr: { + bundle.cached_css_file_array.get() orelse arr: { const js = dev.generateCssJSArray(route_bundle) catch bun.outOfMemory(); - route_bundle.cached_css_file_array = JSC.Strong.create(js, dev.vm.global); + bundle.cached_css_file_array = JSC.Strong.create(js, dev.vm.global); break :arr js; }, }, ); } -pub fn onJsRequestWithBundle(dev: *DevServer, bundle_index: RouteBundle.Index, resp: *Response) void { +fn onHtmlRequestWithBundle(dev: *DevServer, route_bundle_index: RouteBundle.Index, resp: AnyResponse, method: bun.http.Method) void { + const route_bundle = dev.routeBundlePtr(route_bundle_index); + assert(route_bundle.data == .html); + const html = &route_bundle.data.html; + const blob = html.cached_response orelse generate: { + const payload = generateHTMLPayload(dev, route_bundle_index, route_bundle, html) catch bun.outOfMemory(); + errdefer dev.allocator.free(payload); + html.cached_response = StaticRoute.initFromAnyBlob( + .fromOwnedSlice(dev.allocator, payload), + .{ + .mime_type = .html, + .server = dev.server orelse unreachable, + }, + ); + break :generate html.cached_response.?; + }; + blob.onWithMethod(method, resp); +} + +fn generateHTMLPayload(dev: *DevServer, route_bundle_index: RouteBundle.Index, route_bundle: *RouteBundle, html: *RouteBundle.HTML) bun.OOM![]u8 { + assert(route_bundle.server_state == .loaded); // if not loaded, following values wont be initialized + assert(html.html_bundle.dev_server_id.unwrap() == route_bundle_index); + assert(html.cached_response == null); + const head_end_tag_index = (html.head_end_tag_index.unwrap() orelse unreachable).get(); + const body_end_tag_index = (html.body_end_tag_index.unwrap() orelse unreachable).get(); + const bundled_html = html.bundled_html_text orelse unreachable; + + // The bundler records two offsets in development mode, splitting the HTML + // file into three chunks. DevServer is able to insert style/script tags + // using the information available in IncrementalGraph. This approach + // allows downstream files to update without re-bundling the HTML file. + // + // + // + // + // Single Page Web App + // {head_end_tag_index} + // + //
+ // {body_end_tag_index} + // + const before_head_end = bundled_html[0..head_end_tag_index]; + const before_body_end = bundled_html[head_end_tag_index..body_end_tag_index]; + const after_body_end = bundled_html[body_end_tag_index..]; + + var display_name = bun.strings.withoutSuffixComptime(bun.path.basename(html.html_bundle.html_bundle.path), ".html"); + // TODO: function for URL safe chars + if (!bun.strings.isAllASCII(display_name)) display_name = "page"; + + dev.graph_safety_lock.lock(); + defer dev.graph_safety_lock.unlock(); + + // Prepare bitsets for tracing + var sfa_state = std.heap.stackFallback(65536, dev.allocator); + const sfa = sfa_state.get(); + var gts = try dev.initGraphTraceState(sfa); + defer gts.deinit(sfa); + // Run tracing + dev.client_graph.reset(); + try dev.traceAllRouteImports(route_bundle, >s, .find_css); + + const css_ids = dev.client_graph.current_css_files.items; + + const payload_size = bundled_html.len + + ("").len * css_ids.len + + "".len + + client_prefix.len + "/".len + + display_name.len + + "-0000000000000000.js".len; + + var array: std.ArrayListUnmanaged(u8) = try std.ArrayListUnmanaged(u8).initCapacity(dev.allocator, payload_size); + errdefer array.deinit(dev.allocator); + array.appendSliceAssumeCapacity(before_head_end); + // Insert all link tags before "" + for (css_ids) |name| { + array.appendSliceAssumeCapacity(""); + } + array.appendSliceAssumeCapacity(before_body_end); + // Insert the client script tag before "" + array.appendSliceAssumeCapacity(""); + array.appendSliceAssumeCapacity(after_body_end); + assert(array.items.len == array.capacity); // incorrect memory allocation size + return array.items; +} + +fn getJavaScriptCodeForHTMLFile( + dev: *DevServer, + index: bun.JSAst.Index, + import_records: []bun.BabyList(bun.ImportRecord), + input_file_sources: []bun.logger.Source, + loaders: []bun.options.Loader, +) bun.OOM![]const u8 { + var sfa_state = std.heap.stackFallback(65536, dev.allocator); + const sfa = sfa_state.get(); + var array: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8).initCapacity(sfa, 65536) catch bun.outOfMemory(); + defer array.deinit(sfa); + const w = array.writer(sfa); + + try w.writeAll(" "); + try bun.js_printer.writeJSONString(input_file_sources[index.get()].path.pretty, @TypeOf(w), w, .utf8); + try w.writeAll("(m) {\n "); + for (import_records[index.get()].slice()) |import| { + if (import.source_index.isValid() and loaders[import.source_index.get()] == .css) continue; + try w.writeAll(" m.dynamicImport("); + try bun.js_printer.writeJSONString(import.path.pretty, @TypeOf(w), w, .utf8); + try w.writeAll(");\n "); + } + try w.writeAll("},\n"); + + // Avoid-recloning if it is was moved to the hap + return if (array.items.ptr == &sfa_state.buffer) + try bun.default_allocator.dupe(u8, array.items) + else + array.items; +} + +pub fn onJsRequestWithBundle(dev: *DevServer, bundle_index: RouteBundle.Index, resp: AnyResponse, method: bun.http.Method) void { const route_bundle = dev.routeBundlePtr(bundle_index); - const code = route_bundle.client_bundle orelse code: { - const code = dev.generateClientBundle(route_bundle) catch bun.outOfMemory(); - route_bundle.client_bundle = code; - break :code code; + const blob = route_bundle.client_bundle orelse generate: { + const payload = dev.generateClientBundle(route_bundle) catch bun.outOfMemory(); + errdefer dev.allocator.free(payload); + route_bundle.client_bundle = StaticRoute.initFromAnyBlob( + .fromOwnedSlice(dev.allocator, payload), + .{ + .mime_type = .javascript, + .server = dev.server orelse unreachable, + }, + ); + break :generate route_bundle.client_bundle.?; }; - sendTextFile(code, MimeType.javascript.value, resp); + blob.onWithMethod(method, resp); } -pub fn onSrcRequest(dev: *DevServer, req: *uws.Request, resp: *App.Response) void { +pub fn onSrcRequest(dev: *DevServer, req: *uws.Request, resp: anytype) void { if (req.header("open-in-editor") == null) { resp.writeStatus("501 Not Implemented"); resp.end("Viewing source without opening in editor is not implemented yet!", false); return; } - const ctx = &dev.vm.rareData().editor_context; - ctx.autoDetectEditor(JSC.VirtualMachine.get().transpiler.env); - const line: ?[]const u8 = req.header("editor-line"); - const column: ?[]const u8 = req.header("editor-column"); + // TODO: better editor detection. on chloe's dev env, this opens apple terminal + vim + resp.writeStatus("501 Not Implemented"); + resp.end("TODO", false); + _ = dev; - if (ctx.editor) |editor| { - var url = req.url()[internal_prefix.len + "/src/".len ..]; - if (bun.strings.indexOfChar(url, ':')) |colon| { - url = url[0..colon]; - } - editor.open(ctx.path, url, line, column, dev.allocator) catch { - resp.writeStatus("202 No Content"); - resp.end("", false); - return; - }; - resp.writeStatus("202 No Content"); - resp.end("", false); - } else { - resp.writeStatus("500 Internal Server Error"); - resp.end("Please set your editor in bunfig.toml", false); - } + // const ctx = &dev.vm.rareData().editor_context; + // ctx.autoDetectEditor(JSC.VirtualMachine.get().transpiler.env); + // const line: ?[]const u8 = req.header("editor-line"); + // const column: ?[]const u8 = req.header("editor-column"); + + // if (ctx.editor) |editor| { + // var url = req.url()[internal_prefix.len + "/src/".len ..]; + // if (bun.strings.indexOfChar(url, ':')) |colon| { + // url = url[0..colon]; + // } + // editor.open(ctx.path, url, line, column, dev.allocator) catch { + // resp.writeStatus("202 No Content"); + // resp.end("", false); + // return; + // }; + // resp.writeStatus("202 No Content"); + // resp.end("", false); + // } else { + // resp.writeStatus("500 Internal Server Error"); + // resp.end("Please set your editor in bunfig.toml", false); + // } } +/// When requests are waiting on a bundle, the relevant request information is +/// prepared and stored in a linked list. const DeferredRequest = struct { + /// A small maximum is set because development servers are unlikely to + /// acquire much load, so allocating a ton at the start for no reason + /// is very silly. This contributes to ~6kb of the initial DevServer allocation. + const max_preallocated = 16; + + pub const List = std.SinglyLinkedList(DeferredRequest); + pub const Node = List.Node; + route_bundle_index: RouteBundle.Index, - data: Data, + handler: Handler, - const Data = union(enum) { + const Handler = union(enum) { + /// For a .framework route. This says to call and render the page. server_handler: bun.JSC.API.SavedRequest, - js_payload: *Response, - - const Tag = @typeInfo(Data).@"union".tag_type.?; + /// For a .html route. Serve the bundled HTML page. + bundled_html_page: ResponseAndMethod, + /// Do nothing and free this node. To simplify lifetimes, + /// the `DeferredRequest` is not freed upon abortion. Which + /// is okay since most requests do not abort. + aborted, + + /// Does not include `aborted` because branching on that value + /// has no meaningful purpose, so it is excluded. + const Kind = enum { + server_handler, + bundled_html_page, + }; }; + + fn onAbort(this: *DeferredRequest, resp: AnyResponse) void { + _ = resp; + this.abort(); + assert(this.handler == .aborted); + } + + /// Calling this is only required if the desired handler is going to be avoided, + /// such as for bundling failures or aborting the server. + /// Does not free the underlying `DeferredRequest.Node` + fn deinit(this: *DeferredRequest) void { + switch (this.handler) { + .server_handler => |*saved| saved.deinit(), + .bundled_html_page, .aborted => {}, + } + } + + /// Deinitializes state by aborting the connection. + fn abort(this: *DeferredRequest) void { + switch (this.handler) { + .server_handler => |*saved| { + saved.response.endWithoutBody(true); + saved.deinit(); + }, + .bundled_html_page => |r| { + r.response.endWithoutBody(true); + }, + .aborted => return, + } + this.handler = .aborted; + } +}; + +const ResponseAndMethod = struct { + response: AnyResponse, + method: bun.http.Method, }; fn startAsyncBundle( @@ -881,6 +1484,9 @@ fn startAsyncBundle( dev.incremental_result.reset(); + // Ref server to keep it from closing. + if (dev.server) |server| server.onPendingRequest(); + var heap = try ThreadlocalArena.init(); errdefer heap.deinit(); const allocator = heap.allocator(); @@ -889,24 +1495,17 @@ fn startAsyncBundle( ast_memory_allocator.reset(); ast_memory_allocator.push(); - if (dev.framework.server_components == null) { - // The handling of the dependency graphs are SLIGHTLY different when - // server components are disabled. It's subtle, but enough that it - // would be incorrect to even try to run a build. - bun.todoPanic(@src(), "support non-server components build", .{}); - } - const bv2 = try BundleV2.init( - &dev.server_bundler, - if (dev.framework.server_components != null) .{ + &dev.server_transpiler, + .{ .framework = dev.framework, - .client_bundler = &dev.client_bundler, - .ssr_bundler = &dev.ssr_bundler, + .client_transpiler = &dev.client_transpiler, + .ssr_transpiler = &dev.ssr_transpiler, .plugins = dev.bundler_options.plugin, - } else @panic("TODO: support non-server components"), + }, allocator, .{ .js = dev.vm.eventLoop() }, - false, // reloading is handled separately + false, // watching is handled separately JSC.WorkPool.get(), heap, ); @@ -928,14 +1527,38 @@ fn startAsyncBundle( .timer = timer, .start_data = start_data, .had_reload_event = had_reload_event, + .requests = dev.next_bundle.requests, + .resolution_failure_entries = .{}, }; - const old_current_requests = dev.current_bundle_requests; - bun.assert(old_current_requests.items.len == 0); - dev.current_bundle_requests = dev.next_bundle.requests; - dev.next_bundle.requests = old_current_requests; + dev.next_bundle.requests = .{}; } fn indexFailures(dev: *DevServer) !void { + // Since resolution failures can be asynchronous, their logs are not inserted + // until the very end. + const resolution_failures = dev.current_bundle.?.resolution_failure_entries; + if (resolution_failures.count() > 0) { + for (resolution_failures.keys(), resolution_failures.values()) |owner, *log| { + if (log.hasErrors()) { + switch (owner.decode()) { + .client => |index| try dev.client_graph.insertFailure(.index, index, log, false), + .server => |index| try dev.server_graph.insertFailure(.index, index, log, true), + .none, .route => unreachable, + } + } + } + } + + // Theoretically, it shouldn't be possible for errors to leak into dev.log, but just in + // case that happens, they can be printed out. + if (dev.log.hasErrors()) { + if (Environment.isDebug) { + Output.debugWarn("dev.log should not be written into when using DevServer", .{}); + } + dev.log.print(Output.errorWriter()) catch {}; + } + + // After inserting failures into the IncrementalGraphs, they are traced to their routes. var sfa_state = std.heap.stackFallback(65536, dev.allocator); const sfa = sfa_state.get(); @@ -973,7 +1596,7 @@ fn indexFailures(dev: *DevServer) !void { } } - for (dev.incremental_result.routes_affected.items) |entry| { + for (dev.incremental_result.framework_routes_affected.items) |entry| { if (dev.router.routePtr(entry.route_index).bundle.unwrap()) |index| { dev.routeBundlePtr(index).server_state = .possible_bundling_failures; } @@ -981,6 +1604,10 @@ fn indexFailures(dev: *DevServer) !void { dev.markAllRouteChildrenFailed(entry.route_index); } + for (dev.incremental_result.html_routes_affected.items) |index| { + dev.routeBundlePtr(index).server_state = .possible_bundling_failures; + } + dev.publish(.errors, payload.items, .binary); } else if (dev.incremental_result.failures_removed.items.len > 0) { var payload = try std.ArrayList(u8).initCapacity(sfa, @sizeOf(MessageId) + @sizeOf(u32) + dev.incremental_result.failures_removed.items.len * @sizeOf(u32)); @@ -1003,7 +1630,7 @@ fn indexFailures(dev: *DevServer) !void { /// Used to generate the entry point. Unlike incremental patches, this always /// contains all needed files for a route. -fn generateClientBundle(dev: *DevServer, route_bundle: *RouteBundle) bun.OOM![]const u8 { +fn generateClientBundle(dev: *DevServer, route_bundle: *RouteBundle) bun.OOM![]u8 { assert(route_bundle.client_bundle == null); assert(route_bundle.server_state == .loaded); // page is unfit to load @@ -1018,19 +1645,65 @@ fn generateClientBundle(dev: *DevServer, route_bundle: *RouteBundle) bun.OOM![]c // Run tracing dev.client_graph.reset(); - try dev.traceAllRouteImports(route_bundle, >s, .{ .find_client_modules = true }); + try dev.traceAllRouteImports(route_bundle, >s, .find_client_modules); + + var react_fast_refresh_id: []const u8 = ""; + if (dev.framework.react_fast_refresh) |rfr| brk: { + const rfr_index = dev.client_graph.getFileIndex(rfr.import_source) orelse + break :brk; + if (!dev.client_graph.stale_files.isSet(rfr_index.get())) { + try dev.client_graph.traceImports(rfr_index, >s, .find_client_modules); + react_fast_refresh_id = dev.relativePath(rfr.import_source); + } + } + + const client_file: ?IncrementalGraph(.client).FileIndex = switch (route_bundle.data) { + .framework => |fw| if (dev.router.typePtr(dev.router.routePtr(fw.route_index).type).client_file.unwrap()) |ofi| + fromOpaqueFileId(.client, ofi) + else + null, + .html => |html| html.bundled_file, + }; - const client_file = dev.router.typePtr(dev.router.routePtr(route_bundle.route).type).client_file.unwrap() orelse - @panic("No client side entrypoint in client bundle"); + const hash = hash: { + var source_map_hash: bun.bundle_v2.ContentHasher.Hash = .init(0x4b10); // arbitrarily different seed than what .initial_response uses + const keys = dev.client_graph.bundled_files.keys(); + for (dev.client_graph.current_chunk_parts.items) |part| { + source_map_hash.update(keys[part.get()]); + source_map_hash.update(dev.client_graph.source_maps.items[part.get()].vlq_chunk.slice()); + } + break :hash source_map_hash.final(); + }; + // Insert the source map + if (try dev.assets.putOrIncrementRefCount(hash, 1)) |static_route_ptr| { + // TODO: this asset is never unreferenced + const source_map = try dev.client_graph.takeSourceMap(.initial_response, sfa, dev.allocator); + errdefer dev.allocator.free(source_map); + static_route_ptr.* = StaticRoute.initFromAnyBlob(.fromOwnedSlice(dev.allocator, source_map), .{ + .server = dev.server.?, + .mime_type = .json, + }); + } - return dev.client_graph.takeBundle( - .initial_response, - dev.relativePath(dev.client_graph.bundled_files.keys()[fromOpaqueFileId(.client, client_file).get()]), - ); + const client_bundle = dev.client_graph.takeJSBundle(.{ + .kind = .initial_response, + .initial_response_entry_point = if (client_file) |index| + dev.relativePath(dev.client_graph.bundled_files.keys()[index.get()]) + else + "", + .react_refresh_entry_point = react_fast_refresh_id, + .source_map_id = hash, + }); + + const source_map = try dev.client_graph.takeSourceMap(.initial_response, sfa, dev.allocator); + dev.allocator.free(source_map); + + return client_bundle; } fn generateCssJSArray(dev: *DevServer, route_bundle: *RouteBundle) bun.OOM!JSC.JSValue { - if (Environment.allow_assert) assert(!route_bundle.cached_css_file_array.has()); + assert(route_bundle.data == .framework); // a JSC.JSValue has no purpose, and therefore isn't implemented. + if (Environment.allow_assert) assert(!route_bundle.data.framework.cached_css_file_array.has()); assert(route_bundle.server_state == .loaded); // page is unfit to load dev.graph_safety_lock.lock(); @@ -1045,39 +1718,50 @@ fn generateCssJSArray(dev: *DevServer, route_bundle: *RouteBundle) bun.OOM!JSC.J // Run tracing dev.client_graph.reset(); - try dev.traceAllRouteImports(route_bundle, >s, .{ .find_css = true }); + try dev.traceAllRouteImports(route_bundle, >s, .find_css); const names = dev.client_graph.current_css_files.items; const arr = JSC.JSArray.createEmpty(dev.vm.global, names.len); for (names, 0..) |item, i| { - const str = bun.String.createUTF8(item); + var buf: [asset_prefix.len + @sizeOf(u64) * 2 + "/.css".len]u8 = undefined; + const path = std.fmt.bufPrint(&buf, asset_prefix ++ "/{s}.css", .{ + &std.fmt.bytesToHex(std.mem.asBytes(&item), .lower), + }) catch unreachable; + const str = bun.String.createUTF8(path); defer str.deref(); arr.putIndex(dev.vm.global, @intCast(i), str.toJS(dev.vm.global)); } return arr; } -fn traceAllRouteImports(dev: *DevServer, route_bundle: *RouteBundle, gts: *GraphTraceState, goal: TraceImportGoal) !void { - var route = dev.router.routePtr(route_bundle.route); - const router_type = dev.router.typePtr(route.type); +fn traceAllRouteImports(dev: *DevServer, route_bundle: *RouteBundle, gts: *GraphTraceState, comptime goal: TraceImportGoal) !void { + switch (route_bundle.data) { + .framework => |fw| { + var route = dev.router.routePtr(fw.route_index); + const router_type = dev.router.typePtr(route.type); - // Both framework entry points are considered - try dev.server_graph.traceImports(fromOpaqueFileId(.server, router_type.server_file), gts, .{ .find_css = true }); - if (router_type.client_file.unwrap()) |id| { - try dev.client_graph.traceImports(fromOpaqueFileId(.client, id), gts, goal); - } + // Both framework entry points are considered + try dev.server_graph.traceImports(fromOpaqueFileId(.server, router_type.server_file), gts, .find_css); + if (router_type.client_file.unwrap()) |id| { + try dev.client_graph.traceImports(fromOpaqueFileId(.client, id), gts, goal); + } - // The route file is considered - if (route.file_page.unwrap()) |id| { - try dev.server_graph.traceImports(fromOpaqueFileId(.server, id), gts, goal); - } + // The route file is considered + if (route.file_page.unwrap()) |id| { + try dev.server_graph.traceImports(fromOpaqueFileId(.server, id), gts, goal); + } - // For all parents, the layout is considered - while (true) { - if (route.file_layout.unwrap()) |id| { - try dev.server_graph.traceImports(fromOpaqueFileId(.server, id), gts, goal); - } - route = dev.router.routePtr(route.parent.unwrap() orelse break); + // For all parents, the layout is considered + while (true) { + if (route.file_layout.unwrap()) |id| { + try dev.server_graph.traceImports(fromOpaqueFileId(.server, id), gts, goal); + } + route = dev.router.routePtr(route.parent.unwrap() orelse break); + } + }, + .html => |html| { + try dev.client_graph.traceImports(html.bundled_file, gts, goal); + }, } } @@ -1138,10 +1822,33 @@ pub const HotUpdateContext = struct { pub fn finalizeBundle( dev: *DevServer, bv2: *bun.bundle_v2.BundleV2, - result: bun.bundle_v2.BakeBundleOutput, + result: bun.bundle_v2.DevServerOutput, ) bun.OOM!void { - defer dev.startNextBundleIfPresent(); + defer { + bv2.deinit(); + dev.current_bundle = null; + + dev.assets.reindexIfNeeded(dev.allocator) catch { + // not fatal: the assets may be reindexed some time later. + }; + + dev.startNextBundleIfPresent(); + + // Unref the ref added in `startAsyncBundle` + if (dev.server) |server| server.onStaticRequestComplete(); + } const current_bundle = &dev.current_bundle.?; + defer { + if (current_bundle.requests.first != null) { + // cannot be an assertion because in the case of error.OutOfMemory, the request list was not drained. + Output.debug("current_bundle.requests.first != null. this leaves pending requests without an error page!", .{}); + } + while (current_bundle.requests.popFirst()) |node| { + defer dev.deferred_request_pool.put(node); + const req = &node.data; + req.abort(); + } + } dev.graph_safety_lock.lock(); defer dev.graph_safety_lock.unlock(); @@ -1185,10 +1892,17 @@ pub fn finalizeBundle( js_chunk.compile_results_for_chunk, ) |part_range, compile_result| { const index = part_range.source_index; + const source_map: SourceMap.Chunk = compile_result.sourceMapChunk() orelse brk: { + // The source map is `null` if empty + bun.assert(compile_result.javascript.result == .result); + bun.assert(dev.server_transpiler.options.source_map != .none); + bun.assert(!part_range.source_index.isRuntime()); + break :brk .empty; + }; switch (targets[part_range.source_index.get()].bakeGraph()) { - .server => try dev.server_graph.receiveChunk(&ctx, index, compile_result.code(), .js, false), - .ssr => try dev.server_graph.receiveChunk(&ctx, index, compile_result.code(), .js, true), - .client => try dev.client_graph.receiveChunk(&ctx, index, compile_result.code(), .js, false), + .server => try dev.server_graph.receiveChunk(&ctx, index, .{ .js = compile_result.code() }, source_map, false), + .ssr => try dev.server_graph.receiveChunk(&ctx, index, .{ .js = compile_result.code() }, source_map, true), + .client => try dev.client_graph.receiveChunk(&ctx, index, .{ .js = compile_result.code() }, source_map, false), } } @@ -1203,16 +1917,41 @@ pub fn finalizeBundle( chunk, result.chunks, null, - false, // TODO: sourcemaps true + false, ); // Create an entry for this file. const key = ctx.sources[index.get()].path.keyForIncrementalGraph(); + // const hash = brk: { + // var hash: ContentHasher.Hash = .init(0x9a4e); // arbitrary seed + // hash.update(key); + // hash.update(code.buffer); + // break :brk hash.final(); + // }; + // TODO: use a hash mix with the first half being a path hash (to identify files) and + // the second half to be the content hash (to know if the file has changed) + const hash = bun.hash(key); + const asset_index = (try dev.assets.replacePath( + key, + .fromOwnedSlice(dev.allocator, code.buffer), + .css, + hash, + )).index; // Later code needs to retrieve the CSS content // The hack is to use `entry_point_id`, which is otherwise unused, to store an index. - chunk.entry_point.entry_point_id = try dev.insertOrUpdateCssAsset(key, code.buffer); + chunk.entry_point.entry_point_id = asset_index; + + // Track css files that look like tailwind files. + if (dev.has_tailwind_plugin_hack) |*map| { + const first_1024 = code.buffer[0..@min(code.buffer.len, 1024)]; + if (std.mem.indexOf(u8, first_1024, "tailwind") != null) { + try map.put(dev.allocator, key, {}); + } else { + _ = map.swapRemove(key); + } + } - try dev.client_graph.receiveChunk(&ctx, index, "", .css, false); + try dev.client_graph.receiveChunk(&ctx, index, .{ .css = hash }, null, false); // If imported on server, there needs to be a server-side file entry // so that edges can be attached. When a file is only imported on @@ -1226,6 +1965,44 @@ pub fn finalizeBundle( } } + for (result.htmlChunks()) |*chunk| { + const index = bun.JSAst.Index.init(chunk.entry_point.source_index); + const compile_result = chunk.compile_results_for_chunk[0].html; + const generated_js = try dev.getJavaScriptCodeForHTMLFile( + index, + import_records, + input_file_sources, + bv2.graph.input_files.items(.loader), + ); + try dev.client_graph.receiveChunk( + &ctx, + index, + .{ .js = generated_js }, + null, // HTML chunk does not have a source map. + false, + ); + const client_index = ctx.getCachedIndex(.client, index).*; + const route_bundle_index = dev.client_graph.htmlRouteBundleIndex(client_index); + const route_bundle = dev.routeBundlePtr(route_bundle_index); + assert(route_bundle.data.html.bundled_file == client_index); + const html = &route_bundle.data.html; + + if (html.cached_response) |blob| { + blob.deref(); + html.cached_response = null; + route_bundle.invalidateClientBundle(); + } + if (html.bundled_html_text) |slice| { + dev.allocator.free(slice); + } + html.bundled_html_text = compile_result.code; + + html.head_end_tag_index = .init(compile_result.offsets.head_end_tag); + html.body_end_tag_index = .init(compile_result.offsets.body_end_tag); + + chunk.entry_point.entry_point_id = @intCast(route_bundle_index.get()); + } + var gts = try dev.initGraphTraceState(bv2.graph.allocator); defer gts.deinit(bv2.graph.allocator); ctx.gts = >s; @@ -1242,9 +2019,14 @@ pub fn finalizeBundle( .client => try dev.client_graph.processChunkDependencies(&ctx, part_range.source_index, bv2.graph.allocator), } } + for (result.htmlChunks()) |*chunk| { + const index = bun.JSAst.Index.init(chunk.entry_point.source_index); + try dev.client_graph.processChunkDependencies(&ctx, index, bv2.graph.allocator); + } for (result.cssChunks(), result.css_file_list.values()) |*chunk, metadata| { const index = bun.JSAst.Index.init(chunk.entry_point.source_index); - // TODO: index css deps + // TODO: index css deps. this must add all recursively referenced files + // as dependencies of the entry point, instead of building a recursive tree. _ = index; _ = metadata; } @@ -1266,8 +2048,8 @@ pub fn finalizeBundle( } // Load all new chunks into the server runtime. - if (dev.server_graph.current_chunk_len > 0) { - const server_bundle = try dev.server_graph.takeBundle(.hmr_chunk, ""); + if (!dev.frontend_only and dev.server_graph.current_chunk_len > 0) { + const server_bundle = try dev.server_graph.takeJSBundle(.{ .kind = .hmr_chunk }); defer dev.allocator.free(server_bundle); const server_modules = c.BakeLoadServerHmrPatch(@ptrCast(dev.vm.global), bun.String.createLatin1(server_bundle)) catch |err| { @@ -1315,7 +2097,8 @@ pub fn finalizeBundle( // It was discovered that if a tree falls with nobody around it, it does not // make any sound. Let's avoid writing into `w` if no sockets are open. - const will_hear_hot_update = dev.numSubscribers(.hot_update) > 0; + const hot_update_subscribers = dev.numSubscribers(.hot_update); + const will_hear_hot_update = hot_update_subscribers > 0; // This list of routes affected excludes client code. This means changing // a client component wont count as a route to trigger a reload on. @@ -1326,20 +2109,25 @@ pub fn finalizeBundle( // clear for those) if (will_hear_hot_update and current_bundle.had_reload_event and - dev.incremental_result.routes_affected.items.len > 0 and + (dev.incremental_result.framework_routes_affected.items.len + + dev.incremental_result.html_routes_affected.items.len) > 0 and dev.bundling_failures.count() == 0) { has_route_bits_set = true; // A bit-set is used to avoid duplicate entries. This is not a problem - // with `dev.incremental_result.routes_affected` - for (dev.incremental_result.routes_affected.items) |request| { + // with `dev.incremental_result.framework_routes_affected` + for (dev.incremental_result.framework_routes_affected.items) |request| { const route = dev.router.routePtr(request.route_index); if (route.bundle.unwrap()) |id| route_bits.set(id.get()); if (request.should_recurse_when_visiting) { markAllRouteChildren(&dev.router, 1, .{&route_bits}, request.route_index); } } + for (dev.incremental_result.html_routes_affected.items) |route_bundle_index| { + route_bits.set(route_bundle_index.get()); + route_bits_client.set(route_bundle_index.get()); + } // List 1 var it = route_bits.iterator(.{ .kind = .set }); @@ -1358,7 +2146,7 @@ pub fn finalizeBundle( if (dev.incremental_result.client_components_affected.items.len > 0) { has_route_bits_set = true; - dev.incremental_result.routes_affected.clearRetainingCapacity(); + dev.incremental_result.framework_routes_affected.clearRetainingCapacity(); gts.clear(); for (dev.incremental_result.client_components_affected.items) |index| { @@ -1367,7 +2155,7 @@ pub fn finalizeBundle( // A bit-set is used to avoid duplicate entries. This is not a problem // with `dev.incremental_result.routes_affected` - for (dev.incremental_result.routes_affected.items) |request| { + for (dev.incremental_result.framework_routes_affected.items) |request| { const route = dev.router.routePtr(request.route_index); if (route.bundle.unwrap()) |id| { route_bits.set(id.get()); @@ -1382,10 +2170,19 @@ pub fn finalizeBundle( var it = route_bits_client.iterator(.{ .kind = .set }); while (it.next()) |bundled_route_index| { const bundle = &dev.route_bundles.items[bundled_route_index]; - if (bundle.client_bundle) |old| { - dev.allocator.free(old); - } - bundle.client_bundle = null; + bundle.invalidateClientBundle(); + } + } else if (dev.incremental_result.html_routes_affected.items.len > 0) { + // When only HTML routes were affected, there may not be any client + // components that got affected, but the bundles for these HTML routes + // are invalid now. That is why HTML routes above writes into + // `route_bits_client`. + + // Free old bundles + var it = route_bits_client.iterator(.{ .kind = .set }); + while (it.next()) |bundled_route_index| { + const bundle = &dev.route_bundles.items[bundled_route_index]; + bundle.invalidateClientBundle(); } } @@ -1398,29 +2195,30 @@ pub fn finalizeBundle( var it = route_bits.iterator(.{ .kind = .set }); // List 2 while (it.next()) |i| { - const bundle = dev.routeBundlePtr(RouteBundle.Index.init(@intCast(i))); + const route_bundle = dev.routeBundlePtr(RouteBundle.Index.init(@intCast(i))); if (dev.incremental_result.had_adjusted_edges) { - bundle.cached_css_file_array.clear(); + switch (route_bundle.data) { + .framework => |*fw_bundle| fw_bundle.cached_css_file_array.clear(), + .html => |*html| if (html.cached_response) |blob| { + blob.deref(); + html.cached_response = null; + }, + } } - if (bundle.active_viewers == 0 or !will_hear_hot_update) continue; + if (route_bundle.active_viewers == 0 or !will_hear_hot_update) continue; try w.writeInt(i32, @intCast(i), .little); - try w.writeInt(u32, @intCast(bundle.full_pattern.len), .little); - try w.writeAll(bundle.full_pattern); // If no edges were changed, then it is impossible to // change the list of CSS files. if (dev.incremental_result.had_adjusted_edges) { gts.clear(); - try dev.traceAllRouteImports(bundle, >s, .{ .find_css = true }); - const names = dev.client_graph.current_css_files.items; - - try w.writeInt(i32, @intCast(names.len), .little); - for (names) |name| { - const css_prefix_slash = css_prefix ++ "/"; - // These slices are url pathnames. The ID can be extracted - bun.assert(name.len == (css_prefix_slash ++ ".css").len + 16); - bun.assert(bun.strings.hasPrefix(name, css_prefix_slash)); - try w.writeAll(name[css_prefix_slash.len..][0..16]); + dev.client_graph.current_css_files.clearRetainingCapacity(); + try dev.traceAllRouteImports(route_bundle, >s, .find_css); + const css_ids = dev.client_graph.current_css_files.items; + + try w.writeInt(i32, @intCast(css_ids.len), .little); + for (css_ids) |css_id| { + try w.writeAll(&std.fmt.bytesToHex(std.mem.asBytes(&css_id), .lower)); } } else { try w.writeInt(i32, -1, .little); @@ -1433,19 +2231,42 @@ pub fn finalizeBundle( const css_chunks = result.cssChunks(); if (will_hear_hot_update) { if (dev.client_graph.current_chunk_len > 0 or css_chunks.len > 0) { - const css_values = dev.css_files.values(); + const asset_values = dev.assets.files.values(); try w.writeInt(u32, @intCast(css_chunks.len), .little); const sources = bv2.graph.input_files.items(.source); for (css_chunks) |chunk| { const key = sources[chunk.entry_point.source_index].path.keyForIncrementalGraph(); try w.writeAll(&std.fmt.bytesToHex(std.mem.asBytes(&bun.hash(key)), .lower)); - const css_data = css_values[chunk.entry_point.entry_point_id]; + const css_data = asset_values[chunk.entry_point.entry_point_id].blob.InternalBlob.bytes.items; try w.writeInt(u32, @intCast(css_data.len), .little); try w.writeAll(css_data); } - if (dev.client_graph.current_chunk_len > 0) - try dev.client_graph.takeBundleToList(.hmr_chunk, &hot_update_payload, ""); + if (dev.client_graph.current_chunk_len > 0) { + const hash = hash: { + var source_map_hash: bun.bundle_v2.ContentHasher.Hash = .init(0x4b12); // arbitrarily different seed than what .initial_response uses + const keys = dev.client_graph.bundled_files.keys(); + for (dev.client_graph.current_chunk_parts.items) |part| { + source_map_hash.update(keys[part.get()]); + source_map_hash.update(dev.client_graph.source_maps.items[part.get()].vlq_chunk.slice()); + } + break :hash source_map_hash.final(); + }; + // Insert the source map + if (try dev.assets.putOrIncrementRefCount(hash, hot_update_subscribers)) |static_route_ptr| { + const source_map = try dev.client_graph.takeSourceMap(.hmr_chunk, bv2.graph.allocator, dev.allocator); + errdefer dev.allocator.free(source_map); + static_route_ptr.* = StaticRoute.initFromAnyBlob(.fromOwnedSlice(dev.allocator, source_map), .{ + .server = dev.server.?, + .mime_type = .json, + }); + } + // Build and send the source chunk + try dev.client_graph.takeJSBundleToList(&hot_update_payload, .{ + .kind = .hmr_chunk, + .source_map_id = hash, + }); + } } else { try w.writeInt(i32, 0, .little); } @@ -1456,17 +2277,21 @@ pub fn finalizeBundle( if (dev.incremental_result.failures_added.items.len > 0) { dev.bundles_since_last_error = 0; - for (dev.current_bundle_requests.items) |*req| { + while (current_bundle.requests.popFirst()) |node| { + defer dev.deferred_request_pool.put(node); + const req = &node.data; + const rb = dev.routeBundlePtr(req.route_bundle_index); rb.server_state = .possible_bundling_failures; - const resp: *Response = switch (req.data) { + const resp: AnyResponse = switch (req.handler) { + .aborted => continue, .server_handler => |*saved| brk: { - const resp = saved.response.TCP; + const resp = saved.response; saved.deinit(); break :brk resp; }, - .js_payload => |resp| resp, + .bundled_html_page => |ram| ram.response, }; resp.corked(sendSerializedFailures, .{ @@ -1479,7 +2304,6 @@ pub fn finalizeBundle( return; } - // TODO: improve this visual feedback if (dev.bundling_failures.count() == 0) { if (current_bundle.had_reload_event) { const clear_terminal = !debug.isVisible(); @@ -1489,35 +2313,53 @@ pub fn finalizeBundle( Output.enableBuffering(); } + if (Environment.isDebug and memoryLog.isVisible()) { + Output.prettyErrorln("DevServer: {}, RSS: {}", .{ + bun.fmt.size(dev.memoryCost(), .{}), + bun.fmt.size(bun.sys.selfProcessMemoryUsage() orelse 0, .{}), + }); + } + dev.bundles_since_last_error += 1; if (dev.bundles_since_last_error > 1) { Output.prettyError("[x{d}] ", .{dev.bundles_since_last_error}); } } else { dev.bundles_since_last_error = 0; + if (Environment.isDebug and memoryLog.isVisible()) { + Output.prettyErrorln("DevServer: {}, RSS: {}", .{ + bun.fmt.size(dev.memoryCost(), .{}), + bun.fmt.size(bun.sys.selfProcessMemoryUsage() orelse 0, .{}), + }); + } } Output.prettyError("{s} in {d}ms", .{ - if (current_bundle.had_reload_event) "Reloaded" else "Bundled route", + if (current_bundle.had_reload_event) + "Reloaded" + else + "Bundled page", @divFloor(current_bundle.timer.read(), std.time.ns_per_ms), }); // Compute a file name to display - const file_name: ?[]const u8, const total_count: usize = if (current_bundle.had_reload_event) - .{ null, 0 } - else first_route_file_name: { - const opaque_id = dev.router.routePtr( - dev.routeBundlePtr(dev.current_bundle_requests.items[0].route_bundle_index) - .route, - ).file_page.unwrap() orelse - break :first_route_file_name .{ null, 0 }; - const server_index = fromOpaqueFileId(.server, opaque_id); - - break :first_route_file_name .{ - dev.relativePath(dev.server_graph.bundled_files.keys()[server_index.get()]), - 0, - }; + const file_name: ?[]const u8 = if (current_bundle.had_reload_event) + dev.relativePath( + bv2.graph.input_files.items(.source)[bv2.graph.entry_points.items[0].get()].path.text, + ) + else switch (dev.routeBundlePtr(current_bundle.requests.first.?.data.route_bundle_index).data) { + .html => |html| dev.relativePath(html.html_bundle.html_bundle.path), + .framework => |fw| file_name: { + const route = dev.router.routePtr(fw.route_index); + const opaque_id = route.file_page.unwrap() orelse + route.file_layout.unwrap() orelse + break :file_name null; + const server_index = fromOpaqueFileId(.server, opaque_id); + const abs_path = dev.server_graph.bundled_files.keys()[server_index.get()]; + break :file_name dev.relativePath(abs_path); + }, }; + const total_count = bv2.graph.entry_points.items.len; if (file_name) |name| { Output.prettyError(": {s}", .{name}); if (total_count > 1) { @@ -1532,26 +2374,29 @@ pub fn finalizeBundle( dev.graph_safety_lock.unlock(); defer dev.graph_safety_lock.lock(); - for (dev.current_bundle_requests.items) |req| { + while (current_bundle.requests.popFirst()) |node| { + defer dev.deferred_request_pool.put(node); + const req = &node.data; + const rb = dev.routeBundlePtr(req.route_bundle_index); rb.server_state = .loaded; - switch (req.data) { - .server_handler => |saved| dev.onRequestWithBundle(req.route_bundle_index, .{ .saved = saved }, saved.response.TCP), - .js_payload => |resp| dev.onJsRequestWithBundle(req.route_bundle_index, resp), + switch (req.handler) { + .aborted => continue, + .server_handler => |saved| dev.onFrameworkRequestWithBundle(req.route_bundle_index, .{ .saved = saved }, saved.response), + .bundled_html_page => |ram| dev.onHtmlRequestWithBundle(req.route_bundle_index, ram.response, ram.method), } } } fn startNextBundleIfPresent(dev: *DevServer) void { // Clear the current bundle - dev.current_bundle = null; + assert(dev.current_bundle == null); dev.log.clearAndFree(); - dev.current_bundle_requests.clearRetainingCapacity(); - dev.emitVisualizerMessageIfNeeded() catch {}; + dev.emitVisualizerMessageIfNeeded(); // If there were pending requests, begin another bundle. - if (dev.next_bundle.reload_event != null or dev.next_bundle.requests.items.len > 0) { + if (dev.next_bundle.reload_event != null or dev.next_bundle.requests.first != null) { var sfb = std.heap.stackFallback(4096, bun.default_allocator); const temp_alloc = sfb.get(); var entry_points: EntryPointList = EntryPointList.empty; @@ -1569,7 +2414,7 @@ fn startNextBundleIfPresent(dev: *DevServer) void { for (dev.next_bundle.route_queue.keys()) |route_bundle_index| { const rb = dev.routeBundlePtr(route_bundle_index); rb.server_state = .bundling; - dev.appendRouteEntryPointsIfNotStale(&entry_points, temp_alloc, rb.route) catch bun.outOfMemory(); + dev.appendRouteEntryPointsIfNotStale(&entry_points, temp_alloc, route_bundle_index) catch bun.outOfMemory(); } dev.startAsyncBundle( @@ -1583,27 +2428,24 @@ fn startNextBundleIfPresent(dev: *DevServer) void { } } -fn insertOrUpdateCssAsset(dev: *DevServer, abs_path: []const u8, code: []const u8) !Chunk.EntryPoint.ID { - const path_hash = bun.hash(abs_path); - const gop = try dev.css_files.getOrPut(dev.allocator, path_hash); - if (gop.found_existing) { - dev.allocator.free(gop.value_ptr.*); - } - gop.value_ptr.* = code; - return @intCast(gop.index); -} - /// Note: The log is not consumed here pub fn handleParseTaskFailure( dev: *DevServer, err: anyerror, graph: bake.Graph, - key: []const u8, + abs_path: []const u8, log: *const Log, ) bun.OOM!void { dev.graph_safety_lock.lock(); defer dev.graph_safety_lock.unlock(); + debug.log("handleParseTaskFailure({}, .{s}, {}, {d} messages)", .{ + err, + @tagName(graph), + bun.fmt.quote(abs_path), + log.msgs.items.len, + }); + if (err == error.FileNotFound) { // Special-case files being deleted. Note that if a // file never existed, resolution would fail first. @@ -1611,28 +2453,51 @@ pub fn handleParseTaskFailure( // TODO: this should walk up the graph one level, and queue all of these // files for re-bundling if they aren't already in the BundleV2 graph. switch (graph) { - .server, .ssr => try dev.server_graph.onFileDeleted(key, log), - .client => try dev.client_graph.onFileDeleted(key, log), + .server, .ssr => try dev.server_graph.onFileDeleted(abs_path, log), + .client => try dev.client_graph.onFileDeleted(abs_path, log), } } else { Output.prettyErrorln("Error{s} while bundling \"{s}\":", .{ if (log.errors +| log.warnings != 1) "s" else "", - dev.relativePath(key), + dev.relativePath(abs_path), }); log.print(Output.errorWriterBuffered()) catch {}; Output.flush(); // Do not index css errors - if (!bun.strings.hasSuffixComptime(key, ".css")) { + if (!bun.strings.hasSuffixComptime(abs_path, ".css")) { switch (graph) { - .server => try dev.server_graph.insertFailure(key, log, false), - .ssr => try dev.server_graph.insertFailure(key, log, true), - .client => try dev.client_graph.insertFailure(key, log, false), + .server => try dev.server_graph.insertFailure(.abs_path, abs_path, log, false), + .ssr => try dev.server_graph.insertFailure(.abs_path, abs_path, log, true), + .client => try dev.client_graph.insertFailure(.abs_path, abs_path, log, false), } } } } +/// Return a log to write resolution failures into. +pub fn getLogForResolutionFailures(dev: *DevServer, abs_path: []const u8, graph: bake.Graph) !*bun.logger.Log { + assert(dev.current_bundle != null); + const current_bundle = &dev.current_bundle.?; + + dev.graph_safety_lock.lock(); + defer dev.graph_safety_lock.unlock(); + + const owner = switch (graph == .client) { + inline else => |is_client| @unionInit( + SerializedFailure.Owner, + if (is_client) "client" else "server", + try (if (is_client) dev.client_graph else dev.server_graph) + .insertStale(abs_path, !is_client and graph == .ssr), + ).encode(), + }; + const gop = try current_bundle.resolution_failure_entries.getOrPut(current_bundle.bv2.graph.allocator, owner); + if (!gop.found_existing) { + gop.value_ptr.* = bun.logger.Log.init(current_bundle.bv2.graph.allocator); + } + return gop.value_ptr; +} + const CacheEntry = struct { kind: FileKind, }; @@ -1685,72 +2550,86 @@ pub fn routeBundlePtr(dev: *DevServer, idx: RouteBundle.Index) *RouteBundle { return &dev.route_bundles.items[idx.get()]; } -fn onRequest(dev: *DevServer, req: *Request, resp: *Response) void { +fn onRequest(dev: *DevServer, req: *Request, resp: anytype) void { var params: FrameworkRouter.MatchedParams = undefined; if (dev.router.matchSlow(req.url(), ¶ms)) |route_index| { - dev.ensureRouteIsBundled(route_index, .server_handler, req, resp) catch bun.outOfMemory(); + dev.ensureRouteIsBundled( + dev.getOrPutRouteBundle(.{ .framework = route_index }) catch bun.outOfMemory(), + .server_handler, + req, + AnyResponse.init(resp), + ) catch bun.outOfMemory(); return; } switch (dev.server.?) { - inline .DebugHTTPServer, .HTTPServer => |s| if (s.config.onRequest != .zero) { - s.onRequest(req, resp); - return; + inline else => |s| { + if (@typeInfo(@TypeOf(s.app.?)).pointer.child.Response != @typeInfo(@TypeOf(resp)).pointer.child) { + unreachable; // mismatch between `is_ssl` with server and response types. optimize these checks out. + } + if (s.config.onRequest != .zero) { + s.onRequest(req, resp); + return; + } }, - else => @panic("TODO: HTTPS"), } sendBuiltInNotFound(resp); } -fn getOrPutRouteBundle(dev: *DevServer, route: Route.Index) !RouteBundle.Index { - if (dev.router.routePtr(route).bundle.unwrap()) |bundle_index| - return bundle_index; +pub fn respondForHTMLBundle(dev: *DevServer, html: *HTMLBundle.HTMLBundleRoute, req: *uws.Request, resp: AnyResponse) !void { + try dev.ensureRouteIsBundled(try dev.getOrPutRouteBundle(.{ .html = html }), .bundled_html_page, req, resp); +} - const full_pattern = full_pattern: { - var buf = bake.PatternBuffer.empty; - var current: *Route = dev.router.routePtr(route); - // This loop is done to avoid prepending `/` at the root - // if there is more than one component. - buf.prependPart(current.part); - if (current.parent.unwrap()) |first| { - current = dev.router.routePtr(first); - while (current.parent.unwrap()) |next| { - buf.prependPart(current.part); - current = dev.router.routePtr(next); - } - } - break :full_pattern try dev.allocator.dupe(u8, buf.slice()); +fn getOrPutRouteBundle(dev: *DevServer, route: RouteBundle.UnresolvedIndex) !RouteBundle.Index { + const index_location: *RouteBundle.Index.Optional = switch (route) { + .framework => |route_index| &dev.router.routePtr(route_index).bundle, + .html => |html| &html.dev_server_id, }; - errdefer dev.allocator.free(full_pattern); + if (index_location.unwrap()) |bundle_index| { + return bundle_index; + } + + dev.graph_safety_lock.lock(); + defer dev.graph_safety_lock.unlock(); - try dev.route_bundles.append(dev.allocator, .{ - .route = route, + const bundle_index = RouteBundle.Index.init(@intCast(dev.route_bundles.items.len)); + + try dev.route_bundles.ensureUnusedCapacity(dev.allocator, 1); + dev.route_bundles.appendAssumeCapacity(.{ + .data = switch (route) { + .framework => |route_index| .{ .framework = .{ + .route_index = route_index, + .evaluate_failure = null, + .cached_module_list = .{}, + .cached_client_bundle_url = .{}, + .cached_css_file_array = .{}, + } }, + .html => |html| brk: { + const incremental_graph_index = try dev.client_graph.insertStaleExtra(html.html_bundle.path, false, true); + dev.client_graph.source_maps.items[incremental_graph_index.get()].extra.empty.html_bundle_route_index = .init(bundle_index.get()); + break :brk .{ .html = .{ + .html_bundle = html, + .bundled_file = incremental_graph_index, + .head_end_tag_index = .none, + .body_end_tag_index = .none, + .cached_response = null, + .bundled_html_text = null, + } }; + }, + }, + .client_script_generation = 0, .server_state = .unqueued, - .full_pattern = full_pattern, .client_bundle = null, - .evaluate_failure = null, - .cached_module_list = .{}, - .cached_client_bundle_url = .{}, - .cached_css_file_array = .{}, .active_viewers = 0, }); - const bundle_index = RouteBundle.Index.init(@intCast(dev.route_bundles.items.len - 1)); - dev.router.routePtr(route).bundle = bundle_index.toOptional(); + index_location.* = bundle_index.toOptional(); return bundle_index; } -fn sendTextFile(code: []const u8, content_type: []const u8, resp: *Response) void { - if (code.len == 0) { - resp.writeStatus("202 No Content"); - resp.writeHeaderInt("Content-Length", 0); - resp.end("", true); - return; - } - - resp.writeStatus("200 OK"); - resp.writeHeader("Content-Type", content_type); - resp.end(code, true); // TODO: You should never call res.end(huge buffer) +fn registerCatchAllHtmlRoute(dev: *DevServer, html: *HTMLBundle.HTMLBundleRoute) !void { + const bundle_index = try getOrPutRouteBundle(dev, .{ .html = html }); + dev.html_router.fallback = bundle_index.toOptional(); } const ErrorPageKind = enum { @@ -1764,10 +2643,22 @@ const ErrorPageKind = enum { fn sendSerializedFailures( dev: *DevServer, - resp: *Response, + resp: AnyResponse, failures: []const SerializedFailure, kind: ErrorPageKind, ) void { + switch (resp) { + inline else => |r| sendSerializedFailuresInner(dev, r, failures, kind), + } +} + +fn sendSerializedFailuresInner( + dev: *DevServer, + resp: anytype, + failures: []const SerializedFailure, + kind: ErrorPageKind, +) void { + // TODO: write to Blob and serve that resp.writeStatus("500 Internal Server Error"); resp.writeHeader("Content-Type", MimeType.html.value); @@ -1783,11 +2674,11 @@ fn sendSerializedFailures( \\ \\ \\ - \\ + \\ \\", .{js_chunk.unique_key}) catch bun.outOfMemory(); + defer allocator.free(script); + element.append(script, true) catch bun.outOfMemory(); + } + } else { + element.onEndTag(endHeadTagHandler, this) catch return true; } - if (this.chunk.getJSChunkForHTML(this.chunks)) |js_chunk| { - const script = std.fmt.allocPrintZ(allocator, "", .{js_chunk.unique_key}) catch bun.outOfMemory(); - defer allocator.free(script); - element.append(script, true) catch bun.outOfMemory(); + return false; + } + + pub fn onBodyTag(this: *@This(), element: *lol.Element) bool { + if (this.linker.dev_server != null) { + element.onEndTag(endBodyTagHandler, this) catch return true; } + return false; } - const processor = HTMLScanner.HTMLProcessor(@This(), true); + fn endHeadTagHandler(_: *lol.EndTag, opaque_this: ?*anyopaque) callconv(.C) lol.Directive { + const this: *@This() = @alignCast(@ptrCast(opaque_this.?)); + this.head_end_tag_index = @intCast(this.output.items.len); + return .@"continue"; + } - pub fn run(this: *@This(), input: []const u8) !void { - processor.run(this, input) catch bun.outOfMemory(); + fn endBodyTagHandler(_: *lol.EndTag, opaque_this: ?*anyopaque) callconv(.C) lol.Directive { + const this: *@This() = @alignCast(@ptrCast(opaque_this.?)); + this.body_end_tag_index = @intCast(this.output.items.len); + return .@"continue"; } }; - var html_loader = HTMLLoader{ + // HTML bundles for Bake must be globally allocated, as it must outlive + // the bundle task. See `DevServer.RouteBundle.HTML.bundled_html_text` + const output_allocator = if (c.dev_server != null) bun.default_allocator else worker.allocator; + + var html_loader: HTMLLoader = .{ .linker = c, .source_index = chunk.entry_point.source_index, .import_records = import_records[chunk.entry_point.source_index].slice(), @@ -10208,24 +10328,29 @@ pub const LinkerContext = struct { .minify_whitespace = c.options.minify_whitespace, .chunk = chunk, .chunks = chunks, - .output = std.ArrayList(u8).init(worker.allocator), + .output = std.ArrayList(u8).init(output_allocator), .current_import_record_index = 0, }; - html_loader.run(sources[chunk.entry_point.source_index].contents) catch bun.outOfMemory(); + HTMLScanner.HTMLProcessor(HTMLLoader, true).run( + &html_loader, + sources[chunk.entry_point.source_index].contents, + ) catch bun.outOfMemory(); return .{ .html = .{ .code = html_loader.output.items, .source_index = chunk.entry_point.source_index, + .offsets = .{ + .head_end_tag = html_loader.head_end_tag_index, + .body_end_tag = html_loader.body_end_tag_index, + }, }, }; } fn postProcessHTMLChunk(ctx: GenerateChunkCtx, worker: *ThreadPool.Worker, chunk: *Chunk) !void { - // This is where we split output into pieces - const c = ctx.c; var j = StringJoiner{ .allocator = worker.allocator, @@ -10315,7 +10440,7 @@ pub const LinkerContext = struct { // Save the offset to the start of the stored JavaScript j.push(compile_result.code(), bun.default_allocator); - if (compile_result.source_map_chunk()) |source_map_chunk| { + if (compile_result.sourceMapChunk()) |source_map_chunk| { if (c.options.source_maps != .none) { try compile_results_for_source_map.append(worker.allocator, CompileResultForSourceMap{ .source_map_chunk = source_map_chunk, @@ -10425,7 +10550,7 @@ pub const LinkerContext = struct { worker.allocator, c.resolver.opts.target, ast.toAST(), - c.source_(chunk.entry_point.source_index), + c.getSource(chunk.entry_point.source_index), print_options, cross_chunk_import_records.slice(), &[_]Part{ @@ -10438,7 +10563,7 @@ pub const LinkerContext = struct { worker.allocator, c.resolver.opts.target, ast.toAST(), - c.source_(chunk.entry_point.source_index), + c.getSource(chunk.entry_point.source_index), print_options, &.{}, &[_]Part{ @@ -10551,8 +10676,8 @@ pub const LinkerContext = struct { switch (c.options.output_format) { .internal_bake_dev => { const start = bun.bake.getHmrRuntime(if (c.options.target.isServerSide()) .server else .client); - j.pushStatic(start); - line_offset.advance(start); + j.pushStatic(start.code); + line_offset.advance(start.code); }, .iife => { // Bun does not do arrow function lowering. So the wrapper can be an arrow. @@ -10662,7 +10787,7 @@ pub const LinkerContext = struct { } else { j.push(compile_result.code(), bun.default_allocator); - if (compile_result.source_map_chunk()) |source_map_chunk| { + if (compile_result.sourceMapChunk()) |source_map_chunk| { if (c.options.source_maps != .none) { try compile_results_for_source_map.append(worker.allocator, CompileResultForSourceMap{ .source_map_chunk = source_map_chunk, @@ -11552,7 +11677,7 @@ pub const LinkerContext = struct { allocator, c.resolver.opts.target, ast.toAST(), - c.source_(source_index), + c.getSource(source_index), print_options, ast.import_records.slice(), &[_]Part{ @@ -12317,33 +12442,22 @@ pub const LinkerContext = struct { }); const module_id = Expr.initIdentifier(ast.module_ref, Logger.Loc.Empty); - // add a marker for the client runtime to tell that this is an ES module - if (ast.exports_kind == .esm) { - try stmts.inside_wrapper_prefix.append(Stmt.alloc(S.SExpr, .{ - .value = Expr.assign( - Expr.init(E.Dot, .{ - .target = Expr.initIdentifier(ast.module_ref, Loc.Empty), - .name = "__esModule", - .name_loc = Loc.Empty, - }, Loc.Empty), - Expr.init(E.Boolean, .{ .value = true }, Loc.Empty), - ), - }, Loc.Empty)); - } - for (part_stmts) |stmt| { switch (stmt.data) { else => { try stmts.inside_wrapper_suffix.append(stmt); }, .s_import => |st| { - // hmr-runtime.ts defines `module.importSync` to be - // a synchronous import. this is different from - // require in that esm <-> cjs is handled - // automatically, instead of with transpiler-added - // annotations like '__commonJS'. + // hmr-runtime.ts defines `module.dynamicImport` to be the + // ESM `import`. this is different from `require` in that + // esm <-> cjs is handled by the runtime instead of via + // transpiler-added annotations like '__commonJS'. These + // annotations couldn't be added since the bundled file + // must not have any reference to it's imports. That way + // changing a module's type does not re-bundle its + // incremental dependencies. // - // this cannot be done in the parse step because the final + // This cannot be done in the parse step because the final // pretty path is not yet known. the other statement types // are not handled here because some of those generate // new local variables (it is too late to do that here). @@ -12375,10 +12489,13 @@ pub const LinkerContext = struct { str.* = Expr.init(E.String, .{ .data = item.alias }, item.name.loc); } - break :call Expr.init(E.Call, .{ + const expr = Expr.init(E.Call, .{ .target = Expr.init(E.Dot, .{ .target = module_id, - .name = if (is_builtin) "importBuiltin" else "importSync", + .name = if (is_builtin) + "importBuiltin" + else + "importStmt", .name_loc = stmt.loc, }, stmt.loc), .args = js_ast.ExprNodeList.init( @@ -12407,6 +12524,10 @@ pub const LinkerContext = struct { }), ), }, stmt.loc); + break :call if (is_builtin) + expr + else + Expr.init(E.Await, .{ .value = expr }, stmt.loc); } else Expr.init(E.Object, .{}, stmt.loc); if (is_bare_import) { @@ -12459,7 +12580,7 @@ pub const LinkerContext = struct { Index.invalid; // referencing everything by array makes the code a lot more annoying :( - const ast: JSAst = c.graph.ast.get(part_range.source_index.get()); + var ast: JSAst = c.graph.ast.get(part_range.source_index.get()); // For Bun Kit, part generation is entirely special cased. // - export wrapping is already done. @@ -12468,6 +12589,21 @@ pub const LinkerContext = struct { if (c.options.output_format == .internal_bake_dev) { bun.assert(!part_range.source_index.isRuntime()); // embedded in HMR runtime + // add a marker for the client runtime to tell that this is an ES module + if (ast.exports_kind == .esm) { + stmts.inside_wrapper_prefix.append(Stmt.alloc(S.SExpr, .{ + .value = Expr.assign( + Expr.init(E.Dot, .{ + .target = Expr.initIdentifier(ast.module_ref, Loc.Empty), + .name = "__esModule", + .name_loc = Loc.Empty, + }, Loc.Empty), + Expr.init(E.Boolean, .{ .value = true }, Loc.Empty), + ), + }, Loc.Empty)) catch bun.outOfMemory(); + ast.top_level_await_keyword = .{ .loc = .{ .start = 0 }, .len = 1 }; + } + for (parts) |part| { c.convertStmtsForChunkForBake(part_range.source_index.get(), stmts, part.stmts, allocator, &ast) catch |err| return .{ .err = err }; @@ -13048,7 +13184,7 @@ pub const LinkerContext = struct { source_index: Index, ) js_printer.PrintResult { const parts_to_print = &[_]Part{ - Part{ .stmts = out_stmts }, + .{ .stmts = out_stmts }, }; const print_options = js_printer.Options{ @@ -13073,13 +13209,14 @@ pub const LinkerContext = struct { .has_run_symbol_renamer = true, .allocator = allocator, + .source_map_allocator = writer.buffer.allocator, .to_esm_ref = to_esm_ref, .to_commonjs_ref = to_commonjs_ref, .require_ref = switch (c.options.output_format) { - .cjs => null, + .cjs => null, // use unbounded global else => runtime_require_ref, }, - .require_or_import_meta_for_source_callback = js_printer.RequireOrImportMeta.Callback.init( + .require_or_import_meta_for_source_callback = .init( LinkerContext, requireOrImportMetaForSource, c, @@ -13104,7 +13241,7 @@ pub const LinkerContext = struct { &printer, ast.target, ast.toAST(), - c.source_(source_index.get()), + c.getSource(source_index.get()), print_options, ast.import_records.slice(), parts_to_print, @@ -13181,13 +13318,7 @@ pub const LinkerContext = struct { // Per CSS chunk: // Remove duplicate rules across files. This must be done in serial, not // in parallel, and must be done from the last rule to the first rule. - if (brk: { - // TODO: Have count of chunks with css on linker context? - for (chunks) |*chunk| { - if (chunk.content == .css) break :brk true; - } - break :brk false; - }) { + if (c.parse_graph.css_file_count > 0) { var wait_group = try c.allocator.create(sync.WaitGroup); wait_group.init(); defer { @@ -13218,13 +13349,17 @@ pub const LinkerContext = struct { .linker = c, .wg = wait_group, }; - batch.push(ThreadPoolLib.Batch.from(&tasks[i].task)); + batch.push(.from(&tasks[i].task)); i += 1; } } wait_group.counter = @as(u32, @truncate(total_count)); c.parse_graph.pool.pool.schedule(batch); wait_group.wait(); + } else if (Environment.isDebug) { + for (chunks) |*chunk| { + bun.assert(chunk.content != .css); + } } } @@ -13289,13 +13424,13 @@ pub const LinkerContext = struct { remaining_part_ranges[0] = .{ .part_range = part_range, - .i = @truncate(i), + .i = @intCast(i), .task = .{ .callback = &generateCompileResultForJSChunk, }, .ctx = chunk_ctx, }; - batch.push(ThreadPoolLib.Batch.from(&remaining_part_ranges[0].task)); + batch.push(.from(&remaining_part_ranges[0].task)); remaining_part_ranges = remaining_part_ranges[1..]; } @@ -13304,13 +13439,13 @@ pub const LinkerContext = struct { for (0..chunk.content.css.imports_in_chunk_in_order.len) |i| { remaining_part_ranges[0] = .{ .part_range = .{}, - .i = @as(u32, @truncate(i)), - .task = ThreadPoolLib.Task{ + .i = @intCast(i), + .task = .{ .callback = &generateCompileResultForCssChunk, }, .ctx = chunk_ctx, }; - batch.push(ThreadPoolLib.Batch.from(&remaining_part_ranges[0].task)); + batch.push(.from(&remaining_part_ranges[0].task)); remaining_part_ranges = remaining_part_ranges[1..]; } @@ -13319,13 +13454,13 @@ pub const LinkerContext = struct { remaining_part_ranges[0] = .{ .part_range = .{}, .i = 0, - .task = ThreadPoolLib.Task{ + .task = .{ .callback = &generateCompileResultForHtmlChunk, }, .ctx = chunk_ctx, }; - batch.push(ThreadPoolLib.Batch.from(&remaining_part_ranges[0].task)); + batch.push(.from(&remaining_part_ranges[0].task)); remaining_part_ranges = remaining_part_ranges[1..]; }, } @@ -13343,7 +13478,7 @@ pub const LinkerContext = struct { c.source_maps.quoted_contents_tasks.len = 0; } - // For dev server, only post-process CSS chunks. + // For dev server, only post-process CSS + HTML chunks. const chunks_to_do = if (is_dev_server) chunks[1..] else chunks; if (!is_dev_server or chunks_to_do.len > 0) { bun.assert(chunks_to_do.len > 0); @@ -13368,6 +13503,7 @@ pub const LinkerContext = struct { // // - Reuse unchanged parts to assemble the full bundle if Cmd+R is used in the browser // - Send only the newly changed code through a socket. + // - Use IncrementalGraph to have full knowledge of referenced CSS files. // // When this isn't the initial bundle, concatenation as usual would produce a // broken module. It is DevServer's job to create and send HMR patches. @@ -14540,7 +14676,7 @@ pub const LinkerContext = struct { // Warn about importing from a file that is known to not have any exports if (status == .cjs_without_exports) { - const source = c.source_(tracker.source_index.get()); + const source = c.getSource(tracker.source_index.get()); c.log.addRangeWarningFmt( source, source.rangeOfIdentifier(named_import.alias_loc.?), @@ -14591,9 +14727,9 @@ pub const LinkerContext = struct { // Report mismatched imports and exports const symbol = c.graph.symbols.get(tracker.import_ref).?; const named_import: js_ast.NamedImport = named_imports[prev_source_index].get(tracker.import_ref).?; - const source = c.source_(prev_source_index); + const source = c.getSource(prev_source_index); - const next_source = c.source_(next_tracker.source_index.get()); + const next_source = c.getSource(next_tracker.source_index.get()); const r = source.rangeOfIdentifier(named_import.alias_loc.?); // Report mismatched imports and exports @@ -15586,7 +15722,7 @@ pub const Chunk = struct { } pub const CodeResult = struct { - buffer: string, + buffer: []u8, shifts: []sourcemap.SourceMapShifts, }; @@ -15935,7 +16071,6 @@ pub const Chunk = struct { pub const CssImportOrder = struct { conditions: BabyList(bun.css.ImportConditions) = .{}, - // TODO: unfuck this condition_import_records: BabyList(ImportRecord) = .{}, kind: union(enum) { @@ -16023,27 +16158,16 @@ pub const Chunk = struct { pub const ImportsFromOtherChunks = std.AutoArrayHashMapUnmanaged(Index.Int, CrossChunkImport.Item.List); - pub const ContentKind = enum { - javascript, - css, - html, - }; - - pub const HtmlChunk = struct {}; - - pub const Content = union(ContentKind) { + pub const Content = union(enum) { javascript: JavaScriptChunk, css: CssChunk, - html: HtmlChunk, + html, pub fn sourcemap(this: *const Content, default: options.SourceMapOption) options.SourceMapOption { return switch (this.*) { .javascript => default, - // TODO: - .css => options.SourceMapOption.none, - - // probably never - .html => options.SourceMapOption.none, + .css => .none, // TODO: css source maps + .html => .none, }; } @@ -16141,6 +16265,13 @@ pub const CompileResult = union(enum) { html: struct { source_index: Index.Int, code: []const u8, + /// Offsets are used for DevServer to inject resources without re-bundling + offsets: struct { + /// The index of the "<" byte of "" + head_end_tag: u32, + /// The index of the "<" byte of "" + body_end_tag: u32, + }, }, pub const empty = CompileResult{ @@ -16168,7 +16299,7 @@ pub const CompileResult = union(enum) { }; } - pub fn source_map_chunk(this: *const CompileResult) ?sourcemap.Chunk { + pub fn sourceMapChunk(this: *const CompileResult) ?sourcemap.Chunk { return switch (this.*) { .javascript => |r| switch (r.result) { .result => |r2| r2.source_map, @@ -16192,9 +16323,11 @@ const CompileResultForSourceMap = struct { source_index: u32, }; -const ContentHasher = struct { +pub const ContentHasher = struct { + pub const Hash = std.hash.XxHash64; + // xxhash64 outperforms Wyhash if the file is > 1KB or so - hasher: std.hash.XxHash64 = std.hash.XxHash64.init(0), + hasher: Hash = .init(0), const log = bun.Output.scoped(.ContentHasher, true); @@ -16262,11 +16395,9 @@ fn getRedirectId(id: u32) ?u32 { if (id == std.math.maxInt(u32)) { return null; } - return id; } -// TODO: this needs to also update `define` and `external`. This whole setup needs to be more resilient. fn targetFromHashbang(buffer: []const u8) ?options.Target { if (buffer.len > "#!/usr/bin/env bun".len) { if (strings.hasPrefixComptime(buffer, "#!/usr/bin/env bun")) { @@ -16276,7 +16407,6 @@ fn targetFromHashbang(buffer: []const u8) ?options.Target { } } } - return null; } @@ -16623,22 +16753,27 @@ pub const CssEntryPointMeta = struct { imported_on_server: bool, }; -/// The lifetime of this structure is tied to the transpiler's arena -pub const BakeBundleStart = struct { +/// The lifetime of this structure is tied to the bundler's arena +pub const DevServerInput = struct { css_entry_points: std.AutoArrayHashMapUnmanaged(Index, CssEntryPointMeta), }; -/// The lifetime of this structure is tied to the transpiler's arena -pub const BakeBundleOutput = struct { +/// The lifetime of this structure is tied to the bundler's arena +pub const DevServerOutput = struct { chunks: []Chunk, css_file_list: std.AutoArrayHashMapUnmanaged(Index, CssEntryPointMeta), + html_files: std.AutoArrayHashMapUnmanaged(Index, void), - pub fn jsPseudoChunk(out: BakeBundleOutput) *Chunk { + pub fn jsPseudoChunk(out: DevServerOutput) *Chunk { return &out.chunks[0]; } - pub fn cssChunks(out: BakeBundleOutput) []Chunk { - return out.chunks[1..]; + pub fn cssChunks(out: DevServerOutput) []Chunk { + return out.chunks[1..][0..out.css_file_list.count()]; + } + + pub fn htmlChunks(out: DevServerOutput) []Chunk { + return out.chunks[1 + out.css_file_list.count() ..][0..out.html_files.count()]; } }; diff --git a/src/cache.zig b/src/cache.zig index 96ecf3484c45de..021f6e5cd34145 100644 --- a/src/cache.zig +++ b/src/cache.zig @@ -45,12 +45,16 @@ const debug = Output.scoped(.fs, false); pub const Fs = struct { pub const Entry = struct { contents: string, - fd: StoredFileDescriptorType = bun.invalid_fd, - external: External = .{}, + fd: StoredFileDescriptorType, + /// When `contents` comes from a native plugin, this field is populated + /// with information on how to free it. + external_free_function: ExternalFreeFunction = .none, - pub const External = struct { - ctx: ?*anyopaque = null, - function: ?*const fn (?*anyopaque) callconv(.C) void = null, + pub const ExternalFreeFunction = struct { + ctx: ?*anyopaque, + function: ?*const fn (?*anyopaque) callconv(.C) void, + + pub const none: ExternalFreeFunction = .{ .ctx = null, .function = null }; pub fn call(this: *const @This()) void { if (this.function) |func| { @@ -60,8 +64,8 @@ pub const Fs = struct { }; pub fn deinit(entry: *Entry, allocator: std.mem.Allocator) void { - if (entry.external.function) |func| { - func(entry.external.ctx); + if (entry.external_free_function.function) |func| { + func(entry.external_free_function.ctx); } else if (entry.contents.len > 0) { allocator.free(entry.contents); entry.contents = ""; diff --git a/src/cli.zig b/src/cli.zig index 844b88e1947d19..f8b42edb10fb40 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -243,6 +243,7 @@ pub const Arguments = struct { clap.parseParam("--throw-deprecation Determine whether or not deprecation warnings result in errors.") catch unreachable, clap.parseParam("--title Set the process title") catch unreachable, clap.parseParam("--zero-fill-buffers Boolean to force Buffer.allocUnsafe(size) to be zero-filled.") catch unreachable, + clap.parseParam("--no-hmr Disable Hot-module-replacement when using HTML imports with Bun.serve") catch unreachable, }; const auto_or_run_params = [_]ParamType{ @@ -818,6 +819,10 @@ pub const Arguments = struct { if (args.flag("--zero-fill-buffers")) { Bun__Node__ZeroFillBuffers = true; } + + if (args.flag("--no-hmr")) { + bun.bake.DevServer.enabled = false; + } } if (opts.port != null and opts.origin == null) { diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig index 0b371a67232890..37e2feb1b27dde 100644 --- a/src/cli/build_command.zig +++ b/src/cli/build_command.zig @@ -235,18 +235,18 @@ pub const BuildCommand = struct { .unspecified => {}, } - var client_bundler: transpiler.Transpiler = undefined; + var client_transpiler: transpiler.Transpiler = undefined; if (this_transpiler.options.server_components) { - client_bundler = try transpiler.Transpiler.init(allocator, log, ctx.args, null); - client_bundler.options = this_transpiler.options; - client_bundler.options.target = .browser; - client_bundler.options.server_components = true; - client_bundler.options.conditions = try this_transpiler.options.conditions.clone(); + client_transpiler = try transpiler.Transpiler.init(allocator, log, ctx.args, null); + client_transpiler.options = this_transpiler.options; + client_transpiler.options.target = .browser; + client_transpiler.options.server_components = true; + client_transpiler.options.conditions = try this_transpiler.options.conditions.clone(); try this_transpiler.options.conditions.appendSlice(&.{"react-server"}); this_transpiler.options.react_fast_refresh = false; this_transpiler.options.minify_syntax = true; - client_bundler.options.minify_syntax = true; - client_bundler.options.define = try options.Define.init( + client_transpiler.options.minify_syntax = true; + client_transpiler.options.define = try options.Define.init( allocator, if (ctx.args.define) |user_defines| try options.Define.Data.fromInput(try options.stringHashMapFromArrays( @@ -262,10 +262,10 @@ pub const BuildCommand = struct { ); try bun.bake.addImportMetaDefines(allocator, this_transpiler.options.define, .development, .server); - try bun.bake.addImportMetaDefines(allocator, client_bundler.options.define, .development, .client); + try bun.bake.addImportMetaDefines(allocator, client_transpiler.options.define, .development, .client); this_transpiler.resolver.opts = this_transpiler.options; - client_bundler.resolver.opts = client_bundler.options; + client_transpiler.resolver.opts = client_transpiler.options; } // var env_loader = this_transpiler.env; diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index 07656cefcfa870..ea181e1e9eb06d 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -1931,7 +1931,7 @@ pub const Example = struct { ), ); - var header_entries: Headers.Entries = .{}; + var header_entries: Headers.Entry.List = .{}; var headers_buf: string = ""; if (env_loader.map.get("GITHUB_TOKEN") orelse env_loader.map.get("GITHUB_ACCESS_TOKEN")) |access_token| { @@ -1939,14 +1939,14 @@ pub const Example = struct { headers_buf = try std.fmt.allocPrint(ctx.allocator, "AuthorizationBearer {s}", .{access_token}); try header_entries.append( ctx.allocator, - Headers.Kv{ - .name = Api.StringPointer{ + .{ + .name = .{ .offset = 0, - .length = @as(u32, @intCast("Authorization".len)), + .length = @intCast("Authorization".len), }, - .value = Api.StringPointer{ - .offset = @as(u32, @intCast("Authorization".len)), - .length = @as(u32, @intCast(headers_buf.len - "Authorization".len)), + .value = .{ + .offset = @intCast("Authorization".len), + .length = @intCast(headers_buf.len - "Authorization".len), }, }, ); diff --git a/src/cli/install.ps1 b/src/cli/install.ps1 index 4d3e36a5792b1d..39c49ac348818c 100644 --- a/src/cli/install.ps1 +++ b/src/cli/install.ps1 @@ -23,7 +23,7 @@ if (-not ((Get-CimInstance Win32_ComputerSystem)).SystemType -match "x64-based") # This corresponds to .win10_rs5 in build.zig $MinBuild = 17763; -$MinBuildName = "Windows 10 1809" +$MinBuildName = "Windows 10 1809 / Windows Server 2019" $WinVer = [System.Environment]::OSVersion.Version if ($WinVer.Major -lt 10 -or ($WinVer.Major -eq 10 -and $WinVer.Build -lt $MinBuild)) { diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index dc9204a14d0cbb..8f38f9eb0f5d51 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -186,10 +186,10 @@ pub const UpgradeCommand = struct { } } - var header_entries: Headers.Entries = .{}; - const accept = Headers.Kv{ - .name = Api.StringPointer{ .offset = 0, .length = @as(u32, @intCast("Accept".len)) }, - .value = Api.StringPointer{ .offset = @as(u32, @intCast("Accept".len)), .length = @as(u32, @intCast("application/vnd.github.v3+json".len)) }, + var header_entries: Headers.Entry.List = .empty; + const accept = Headers.Entry{ + .name = .{ .offset = 0, .length = @intCast("Accept".len) }, + .value = .{ .offset = @intCast("Accept".len), .length = @intCast("application/vnd.github.v3+json".len) }, }; try header_entries.append(allocator, accept); defer if (comptime silent) header_entries.deinit(allocator); @@ -217,14 +217,14 @@ pub const UpgradeCommand = struct { headers_buf = try std.fmt.allocPrint(allocator, default_github_headers ++ "AuthorizationBearer {s}", .{access_token}); try header_entries.append( allocator, - Headers.Kv{ - .name = Api.StringPointer{ + .{ + .name = .{ .offset = accept.value.offset + accept.value.length, - .length = @as(u32, @intCast("Authorization".len)), + .length = @intCast("Authorization".len), }, - .value = Api.StringPointer{ - .offset = @as(u32, @intCast(accept.value.offset + accept.value.length + "Authorization".len)), - .length = @as(u32, @intCast("Bearer ".len + access_token.len)), + .value = .{ + .offset = @intCast(accept.value.offset + accept.value.length + "Authorization".len), + .length = @intCast("Bearer ".len + access_token.len), }, }, ); diff --git a/src/codegen/bake-codegen.ts b/src/codegen/bake-codegen.ts index a48fbaae4e4750..21d1aa607e00ae 100644 --- a/src/codegen/bake-codegen.ts +++ b/src/codegen/bake-codegen.ts @@ -37,6 +37,7 @@ async function run() { entrypoints: [join(base_dir, `hmr-runtime-${file}.ts`)], define: { side: JSON.stringify(side), + IS_ERROR_RUNTIME: String(file === "error"), IS_BUN_DEVELOPMENT: String(!!debug), }, minify: { @@ -120,7 +121,7 @@ async function run() { code = debug ? `((${params}) => {${code}})\n` : `((${params})=>{${code}})\n`; } else { - code = debug ? `((${names}) => {${code}})({\n` : `((${names})=>{${code}})({`; + code = debug ? `(async (${names}) => {${code}})({\n` : `(async(${names})=>{${code}})({`; } } @@ -139,6 +140,7 @@ async function run() { { kind: ["error"], result: results[2] }, ] .filter(x => x.result.status === "rejected") + // @ts-ignore .map(x => ({ kind: x.kind, err: x.result.reason })) as Err[]; if (failed.length > 0) { const flattened_errors: Err[] = []; diff --git a/src/crash_handler.zig b/src/crash_handler.zig index 982a2970a8abde..f8aef7da0018d9 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -1687,6 +1687,12 @@ pub const StoredTrace = struct { var frame = stored.trace(); std.debug.captureStackTrace(begin orelse @returnAddress(), &frame); stored.index = frame.index; + for (frame.instruction_addresses[0..frame.index], 0..) |addr, i| { + if (addr == 0) { + stored.index = i; + break; + } + } return stored; } diff --git a/src/deps/libuv.zig b/src/deps/libuv.zig index 011be6e3823f35..336b5720d77a51 100644 --- a/src/deps/libuv.zig +++ b/src/deps/libuv.zig @@ -2274,8 +2274,7 @@ pub const uv_stdio_container_t = struct_uv_stdio_container_s; pub const uv_process_options_t = extern struct { exit_cb: uv_exit_cb, file: [*:0]const u8, - // TODO(@paperdave): upstream changing libuv's args to const - // it is not mutated in any of their code + // In libuv, this is not 'const', but they never mutate it. args: [*:null]?[*:0]const u8, env: [*:null]?[*:0]const u8, cwd: [*:0]const u8, diff --git a/src/deps/lol-html.zig b/src/deps/lol-html.zig index 303588edc09276..489f2e99c04d6a 100644 --- a/src/deps/lol-html.zig +++ b/src/deps/lol-html.zig @@ -732,8 +732,8 @@ pub const Comment = opaque { }; pub const Directive = enum(c_uint) { - stop = 0, - @"continue" = 1, + @"continue" = 0, + stop = 1, }; pub const lol_html_comment_handler_t = *const fn (*Comment, ?*anyopaque) callconv(.C) Directive; pub const lol_html_text_handler_handler_t = *const fn (*TextChunk, ?*anyopaque) callconv(.C) Directive; diff --git a/src/deps/uws.zig b/src/deps/uws.zig index a56e2cbf3847fa..a64dc65f52a950 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -3039,7 +3039,7 @@ pub const WebSocketBehavior = extern struct { pub fn onMessage(raw_ws: *RawWebSocket, message: [*c]const u8, length: usize, opcode: Opcode) callconv(.C) void { const ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); const this = ws.as(Type).?; - @call(.always_inline, Type.onMessage, .{ + @call(bun.callmod_inline, Type.onMessage, .{ this, ws, if (length > 0) message[0..length] else "", @@ -3079,7 +3079,7 @@ pub const WebSocketBehavior = extern struct { pub fn onClose(raw_ws: *RawWebSocket, code: i32, message: [*c]const u8, length: usize) callconv(.C) void { const ws = @unionInit(AnyWebSocket, active_field_name, @as(*WebSocket, @ptrCast(raw_ws))); const this = ws.as(Type).?; - @call(.always_inline, Type.onClose, .{ + @call(bun.callmod_inline, Type.onClose, .{ this, ws, code, @@ -3088,7 +3088,7 @@ pub const WebSocketBehavior = extern struct { } pub fn onUpgrade(ptr: *anyopaque, res: *uws_res, req: *Request, context: *uws_socket_context_t, id: usize) callconv(.C) void { - @call(.always_inline, Server.onWebSocketUpgrade, .{ + @call(bun.callmod_inline, Server.onWebSocketUpgrade, .{ bun.cast(*Server, ptr), @as(*NewApp(is_ssl).Response, @ptrCast(res)), req, @@ -3191,7 +3191,7 @@ pub const AnyResponse = union(enum) { SSL: *NewApp(true).Response, TCP: *NewApp(false).Response, - pub fn init(response: anytype) AnyResponse { + pub inline fn init(response: anytype) AnyResponse { return switch (@TypeOf(response)) { *NewApp(true).Response => .{ .SSL = response }, *NewApp(false).Response => .{ .TCP = response }, @@ -3220,12 +3220,7 @@ pub const AnyResponse = union(enum) { }; } - pub fn write(this: AnyResponse, data: []const u8) void { - return switch (this) { - .SSL => |resp| resp.write(data), - .TCP => |resp| resp.write(data), - }; - } + pub const write = @compileError("this function is not provided to discourage repeatedly checking the response type. use `switch(...) { inline else => ... }` so that multiple calls"); pub fn end(this: AnyResponse, data: []const u8, close_connection: bool) void { return switch (this) { @@ -3292,7 +3287,7 @@ pub const AnyResponse = union(enum) { }; } - pub fn onAborted(this: AnyResponse, comptime UserDataType: type, comptime handler: fn (UserDataType, AnyResponse) void, opcional_data: UserDataType) void { + pub fn onAborted(this: AnyResponse, comptime UserDataType: type, comptime handler: fn (UserDataType, AnyResponse) void, optional_data: UserDataType) void { const wrapper = struct { pub fn ssl_handler(user_data: UserDataType, resp: *NewApp(true).Response) void { handler(user_data, .{ .SSL = resp }); @@ -3302,8 +3297,8 @@ pub const AnyResponse = union(enum) { } }; return switch (this) { - .SSL => |resp| resp.onAborted(UserDataType, wrapper.ssl_handler, opcional_data), - .TCP => |resp| resp.onAborted(UserDataType, wrapper.tcp_handler, opcional_data), + .SSL => |resp| resp.onAborted(UserDataType, wrapper.ssl_handler, optional_data), + .TCP => |resp| resp.onAborted(UserDataType, wrapper.tcp_handler, optional_data), }; } @@ -3357,7 +3352,8 @@ pub const AnyResponse = union(enum) { }; pub fn NewApp(comptime ssl: bool) type { return opaque { - const ssl_flag = @as(i32, @intFromBool(ssl)); + pub const is_ssl = ssl; + const ssl_flag: i32 = @intFromBool(ssl); const ThisApp = @This(); pub fn close(this: *ThisApp) void { diff --git a/src/feature_flags.zig b/src/feature_flags.zig index 8b42968a31ba1e..7a5c7605cc3685 100644 --- a/src/feature_flags.zig +++ b/src/feature_flags.zig @@ -13,9 +13,6 @@ pub const jsx_runtime_is_cjs = true; pub const tracing = true; -// TODO: remove this flag, it should use bun.Output.scoped -pub const verbose_watcher = false; - pub const css_supports_fence = true; pub const enable_entry_cache = true; @@ -155,12 +152,13 @@ pub fn isLibdeflateEnabled() bool { return !bun.getRuntimeFeatureFlag("BUN_FEATURE_FLAG_NO_LIBDEFLATE"); } -/// Enable Bun Kit's experimental bundler tools. +/// Enable the "app" option in Bun.serve. This option will likely be removed +/// in favor of HTML loaders and configuring framework options in bunfig.toml pub fn bake() bool { // In canary or if an environment variable is specified. return env.is_canary or env.isDebug or bun.getRuntimeFeatureFlag("BUN_FEATURE_FLAG_EXPERIMENTAL_BAKE"); } -/// Additional debugging features for Bake, such as the incremental visualizer. +/// Additional debugging features for bake.DevServer, such as the incremental visualizer. /// To use them, extra flags are passed in addition to this one. pub const bake_debugging_features = env.is_canary or env.isDebug; diff --git a/src/fmt.zig b/src/fmt.zig index 179c29a26ef0ff..2720f0b6f9e062 100644 --- a/src/fmt.zig +++ b/src/fmt.zig @@ -1491,12 +1491,12 @@ pub const SizeFormatter = struct { } }; -pub fn size(value: anytype, opts: SizeFormatter.Options) SizeFormatter { +pub fn size(bytes: anytype, opts: SizeFormatter.Options) SizeFormatter { return .{ - .value = switch (@TypeOf(value)) { - f64, f32, f128 => @intFromFloat(value), - i64, isize => @intCast(value), - else => value, + .value = switch (@TypeOf(bytes)) { + f64, f32, f128 => @intFromFloat(bytes), + i64, isize => @intCast(bytes), + else => bytes, }, .opts = opts, }; diff --git a/src/fs.zig b/src/fs.zig index fa20e43a5272aa..a0b5344df55bc3 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -412,18 +412,18 @@ pub const FileSystem = struct { // } pub fn normalize(_: *@This(), str: string) string { - return @call(bun.callmod_inline, path_handler.normalizeString, .{ str, true, .auto }); + return @call(bun.callmod_inline, path_handler.normalizeString, .{ str, true, bun.path.Platform.auto }); } pub fn normalizeBuf(_: *@This(), buf: []u8, str: string) string { - return @call(bun.callmod_inline, path_handler.normalizeStringBuf, .{ str, buf, false, .auto, false }); + return @call(bun.callmod_inline, path_handler.normalizeStringBuf, .{ str, buf, false, bun.path.Platform.auto, false }); } pub fn join(_: *@This(), parts: anytype) string { return @call(bun.callmod_inline, path_handler.joinStringBuf, .{ &join_buf, parts, - .loose, + bun.path.Platform.loose, }); } @@ -431,7 +431,7 @@ pub const FileSystem = struct { return @call(bun.callmod_inline, path_handler.joinStringBuf, .{ buf, parts, - .loose, + bun.path.Platform.loose, }); } diff --git a/src/hive_array.zig b/src/hive_array.zig index 375e7929c811a4..a39e0702e6a5db 100644 --- a/src/hive_array.zig +++ b/src/hive_array.zig @@ -10,9 +10,15 @@ const testing = std.testing; pub fn HiveArray(comptime T: type, comptime capacity: u16) type { return struct { const Self = @This(); - buffer: [capacity]T = undefined, - available: bun.bit_set.IntegerBitSet(capacity) = bun.bit_set.IntegerBitSet(capacity).initFull(), + + buffer: [capacity]T, + available: bun.bit_set.IntegerBitSet(capacity), + pub const size = capacity; + pub const empty: Self = .{ + .buffer = undefined, + .available = .initFull(), + }; pub fn init() Self { return .{}; @@ -75,7 +81,7 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { pub fn init(allocator: std.mem.Allocator) This { return .{ .allocator = allocator, - .hive = if (capacity > 0) HiveArray(T, capacity).init(), + .hive = if (capacity > 0) .empty, }; } @@ -86,7 +92,7 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { } } - return self.allocator.create(T) catch unreachable; + return self.allocator.create(T) catch bun.outOfMemory(); } pub fn getAndSeeIfNew(self: *This, new: *bool) *T { @@ -97,7 +103,7 @@ pub fn HiveArray(comptime T: type, comptime capacity: u16) type { } } - return self.allocator.create(T) catch unreachable; + return self.allocator.create(T) catch bun.outOfMemory(); } pub fn tryGet(self: *This) !*T { diff --git a/src/http.zig b/src/http.zig index efb9255ba75e6c..6eb64576ac4b91 100644 --- a/src/http.zig +++ b/src/http.zig @@ -410,7 +410,7 @@ const ProxyTunnel = struct { }; const pending = encoded_data[@intCast(written)..]; if (pending.len > 0) { - // lets flush when we are trully writable + // lets flush when we are truly writable proxy.write_buffer.write(pending) catch bun.outOfMemory(); } } @@ -583,7 +583,7 @@ fn NewHTTPContext(comptime ssl: bool) type { return ActiveSocket.init(&dead_socket); } - pending_sockets: HiveArray(PooledSocket, pool_size) = HiveArray(PooledSocket, pool_size).init(), + pending_sockets: HiveArray(PooledSocket, pool_size) = .empty, us_socket_context: *uws.SocketContext, const Context = @This(); @@ -1767,7 +1767,7 @@ pub inline fn cleanup(force: bool) void { default_arena.gc(force); } -pub const Headers = @import("./http/headers.zig"); +pub const Headers = JSC.WebCore.Headers; pub const SOCKET_FLAGS: u32 = if (Environment.isLinux) SOCK.CLOEXEC | posix.MSG.NOSIGNAL @@ -2226,7 +2226,7 @@ pub const Flags = packed struct { // TODO: reduce the size of this struct // Many of these fields can be moved to a packed struct and use less space method: Method, -header_entries: Headers.Entries, +header_entries: Headers.Entry.List, header_buf: string, url: URL, connected_url: URL = URL{}, @@ -2400,8 +2400,8 @@ pub const HTTPChannelContext = struct { pub const AsyncHTTP = struct { request: ?picohttp.Request = null, response: ?picohttp.Response = null, - request_headers: Headers.Entries = Headers.Entries{}, - response_headers: Headers.Entries = Headers.Entries{}, + request_headers: Headers.Entry.List = .empty, + response_headers: Headers.Entry.List = .empty, response_buffer: *MutableString, request_body: HTTPRequestBody = .{ .bytes = "" }, allocator: std.mem.Allocator, @@ -2551,7 +2551,7 @@ pub const AsyncHTTP = struct { allocator: std.mem.Allocator, method: Method, url: URL, - headers: Headers.Entries, + headers: Headers.Entry.List, headers_buf: string, response_buffer: *MutableString, request_body: []const u8, @@ -2671,7 +2671,7 @@ pub const AsyncHTTP = struct { allocator: std.mem.Allocator, method: Method, url: URL, - headers: Headers.Entries, + headers: Headers.Entry.List, headers_buf: string, response_buffer: *MutableString, request_body: []const u8, diff --git a/src/http/header_builder.zig b/src/http/header_builder.zig index 94744fc3263e5e..247bcf1cadc132 100644 --- a/src/http/header_builder.zig +++ b/src/http/header_builder.zig @@ -1,15 +1,15 @@ const HeaderBuilder = @This(); const StringBuilder = @import("../string_builder.zig"); -const Headers = @import("./headers.zig"); +const Headers = bun.JSC.WebCore.Headers; const string = bun.string; const HTTPClient = @import("../http.zig"); const Api = @import("../api/schema.zig").Api; const std = @import("std"); const bun = @import("root").bun; -content: StringBuilder = StringBuilder{}, +content: StringBuilder = .{}, header_count: u64 = 0, -entries: Headers.Entries = Headers.Entries{}, +entries: Headers.Entry.List = .empty, pub fn count(this: *HeaderBuilder, name: string, value: string) void { this.header_count += 1; @@ -34,7 +34,7 @@ pub fn append(this: *HeaderBuilder, name: string, value: string) void { .length = @as(u32, @truncate(value.len)), }; _ = this.content.append(value); - this.entries.appendAssumeCapacity(Headers.Kv{ .name = name_ptr, .value = value_ptr }); + this.entries.appendAssumeCapacity(.{ .name = name_ptr, .value = value_ptr }); } pub fn appendFmt(this: *HeaderBuilder, name: string, comptime fmt: string, args: anytype) void { @@ -52,7 +52,7 @@ pub fn appendFmt(this: *HeaderBuilder, name: string, comptime fmt: string, args: .length = @as(u32, @truncate(value.len)), }; - this.entries.appendAssumeCapacity(Headers.Kv{ .name = name_ptr, .value = value_ptr }); + this.entries.appendAssumeCapacity(.{ .name = name_ptr, .value = value_ptr }); } pub fn apply(this: *HeaderBuilder, client: *HTTPClient) void { diff --git a/src/http/headers.zig b/src/http/headers.zig deleted file mode 100644 index fe1f08e98b5c8c..00000000000000 --- a/src/http/headers.zig +++ /dev/null @@ -1,8 +0,0 @@ -const Api = @import("../api/schema.zig").Api; -const std = @import("std"); -const bun = @import("root").bun; -pub const Kv = struct { - name: Api.StringPointer, - value: Api.StringPointer, -}; -pub const Entries = bun.MultiArrayList(Kv); diff --git a/src/http/mime_type.zig b/src/http/mime_type.zig index 1f284dc7ee4769..084ebfe1a7f78f 100644 --- a/src/http/mime_type.zig +++ b/src/http/mime_type.zig @@ -239,6 +239,11 @@ pub fn byExtensionNoDefault(ext: string) ?MimeType { return extensions.get(ext); } +pub fn detectFromPath(path: string) MimeType { + const ext = std.fs.path.extension(path); + return byExtension(ext); +} + // this is partially auto-generated pub const all = struct { pub const @"application/webassembly" = wasm; diff --git a/src/import_record.zig b/src/import_record.zig index a81f5f180b073d..ab1ff7958dbd27 100644 --- a/src/import_record.zig +++ b/src/import_record.zig @@ -196,8 +196,6 @@ pub const ImportRecord = struct { with_type_toml, with_type_file, - tailwind, - pub fn loader(this: Tag) ?bun.options.Loader { return switch (this) { .with_type_sqlite => .sqlite, diff --git a/src/install/install.zig b/src/install/install.zig index 3bbbeb80eeccd9..f5a268871543b3 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -2698,8 +2698,8 @@ pub const PackageManager = struct { pending_pre_calc_hashes: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), pending_tasks: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), total_tasks: u32 = 0, - preallocated_network_tasks: PreallocatedNetworkTasks = PreallocatedNetworkTasks.init(bun.default_allocator), - preallocated_resolve_tasks: PreallocatedTaskStore = PreallocatedTaskStore.init(bun.default_allocator), + preallocated_network_tasks: PreallocatedNetworkTasks = .init(bun.default_allocator), + preallocated_resolve_tasks: PreallocatedTaskStore = .init(bun.default_allocator), /// items are only inserted into this if they took more than 500ms lifecycle_script_time_log: LifecycleScriptTimeLog = .{}, diff --git a/src/js_ast.zig b/src/js_ast.zig index 530eb4ee54ebd2..38105e00907294 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -1301,8 +1301,8 @@ pub const Symbol = struct { .{ symbol.original_name, @tagName(symbol.kind), if (symbol.hasLink()) symbol.link else Ref{ - .source_index = @as(Ref.Int, @truncate(i)), - .inner_index = @as(Ref.Int, @truncate(inner_index)), + .source_index = @truncate(i), + .inner_index = @truncate(inner_index), .tag = .symbol, }, }, @@ -1614,7 +1614,7 @@ pub const E = struct { pub fn hasSameFlagsAs(a: *Dot, b: *Dot) bool { return (a.optional_chain == b.optional_chain and a.is_direct_eval == b.is_direct_eval and - a.can_be_unwrapped_if_unused == b.can_be_unwrapped_if_unused and a.call_can_be_unwrapped_if_unused == b.call_can_be_unwrapped_if_unused); + a.can_be_removed_if_unused == b.can_be_removed_if_unused and a.call_can_be_unwrapped_if_unused == b.call_can_be_unwrapped_if_unused); } }; @@ -1648,7 +1648,7 @@ pub const E = struct { must_keep_due_to_with_stmt: bool = false, // If true, this identifier is known to not have a side effect (i.e. to not - // throw an exception) when referenced. If false, this identifier may or may + // throw an exception) when referenced. If false, this identifier may or // not have side effects when referenced. This is used to allow the removal // of known globals such as "Object" if they aren't used. can_be_removed_if_unused: bool = false, @@ -2027,12 +2027,12 @@ pub const E = struct { return error.Clobber; }, .e_object => |object| { - if (rope.next == null) { - // success - return existing; + if (rope.next != null) { + return try object.getOrPutObject(rope.next.?, allocator); } - return try object.getOrPutObject(rope.next.?, allocator); + // success + return existing; }, else => { return error.Clobber; diff --git a/src/js_parser.zig b/src/js_parser.zig index c99fd26db8bf12..b7a62f08644345 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -1470,12 +1470,6 @@ pub const ImportScanner = struct { } } } - - // when bundling, all top-level variables become var - // TODO(@paperdave): we already do this earlier in visiting? - if (!hot_module_reloading_transformations and p.options.bundle and !st.kind.isUsing()) { - st.kind = .k_var; - } }, .s_export_default => |st| { // This is defer'd so that we still record export default for identifiers @@ -3279,7 +3273,7 @@ pub const Parser = struct { // The lexer location won't be totally accurate, but it's kind of helpful. try p.log.addError(p.source, p.lexer.loc(), "Maximum call stack size exceeded"); - // Return a SyntaxError so that we reuse existing code for handling erorrs. + // Return a SyntaxError so that we reuse existing code for handling errors. return error.SyntaxError; } @@ -5104,136 +5098,6 @@ fn NewParser_( }; }; - /// "Fast Refresh" is React's solution for hot-module-reloading in the context of the UI framework - /// user guide: https://reactnative.dev/docs/fast-refresh (applies to react-dom and native) - /// - /// This depends on performing a couple extra transformations at bundle time, as well as - /// including the `react-refresh` NPM package, which is able to do the heavy lifting, - /// integrating with `react` and `react-dom`. - /// - /// Prior implementations: - /// [1]: https://github.com/facebook/react/blob/main/packages/react-refresh/src/ReactFreshBabelPlugin.js - /// [2]: https://github.com/swc-project/swc/blob/main/crates/swc_ecma_transforms_react/src/refresh/mod.rs - /// - /// Additional reading: - /// [3] https://github.com/facebook/react/issues/16604#issuecomment-528663101 - /// [4] https://github.com/facebook/react/blob/master/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js - /// - /// Instead of a plugin which visits the tree separately, Bun's implementation of fast refresh - /// happens in tandem with the visit pass. The responsibilities of the transform are as follows: - /// - /// 1. For all Components (which is defined as any top-level function/function variable, that is - /// named with a capital letter; see `isComponentishName`), register them to the runtime using - /// `$RefreshReg$(ComponentFunction, "Component");`. Implemented in `p.handleReactRefreshRegister` - /// HOC components are also registered, but only through a special case for `export default` - /// - /// 2. For all functions which call a Hook (a hook is an identifier matching /^use[A-Z]/): - /// a. Outside of the function, create a signature function `const _s = $RefreshSig$();` - /// b. At the start of the function, call `_s()` - /// c. Record all of the hooks called, the variables they are assigned to, and - /// arguments depending on which hook has been used. `useState` and `useReducer`, - /// for example, are special-cased. - /// d. Directly after the function, call `_s(hook, "", forceReset)` - /// - If a user-defined hook is called, the alterate form is used: - /// `_s(hook, "", forceReset, () => [useCustom1, useCustom2])` - /// - /// The upstream transforms do not declare `$RefreshReg$` or `$RefreshSig$`. A typical - /// implementation might look like this, prepending this data to the module start: - /// - /// import * as Refresh from 'react-refresh/runtime'; - /// const $RefreshReg$ = (type, id) => Refresh.register(type, "" + id); - /// const $RefreshSig$ = Refresh.createSignatureFunctionForTransform; - /// - /// Since Bun is a transpiler *and* bundler, we take a slightly different approach. Aside - /// from including the link to the refresh runtime, our notation of $RefreshReg$ is just - /// pointing at `Refresh.register`, which means when we call it, the second argument has - /// to be a string containing the filepath, not just the component name. - const ReactRefresh = struct { - // Set if this JSX/TSX file uses the refresh runtime. If so, - // we must insert an import statement to it. - register_used: bool = false, - signature_used: bool = false, - - /// $RefreshReg$ is called on all top-level variables that are - /// components, as well as HOCs found in the `export default` clause. - register_ref: Ref = Ref.None, - - /// $RefreshSig$ is called to create a signature function, which is - /// used by the refresh runtime to perform smart hook tracking. - create_signature_ref: Ref = Ref.None, - - /// If a comment with '@refresh reset' is seen, we will forward a - /// force refresh to the refresh runtime. This lets you reset the - /// state of hooks on an update on a per-component basis. - // TODO: this is never set - force_reset: bool = false, - - /// The last hook that was scanned. This is used when visiting - /// `.s_local`, as we must hash the variable destructure if the - /// hook's result is assigned directly to a local. - last_hook_seen: ?*E.Call = null, - - /// Every function sets up stack memory to hold data related to it's - /// hook tracking. This is a pointer to that ?HookContext, where an - /// inner null means there are no hook calls. - /// - /// The inner value is initialized when the first hook .e_call is - /// visited, where the '_s' symbol is reserved. Additional hook calls - /// append to the `hasher` and `user_hooks` as needed. - /// - /// When a function is done visiting, the stack location is checked, - /// and then it will insert `var _s = ...`, add the `_s()` call at - /// the start of the function, and then add the call to `_s(func, ...)`. - hook_ctx_storage: ?*?HookContext = null, - - pub const HookContext = struct { - hasher: std.hash.Wyhash, - signature_cb: Ref, - user_hooks: std.AutoArrayHashMapUnmanaged(Ref, Expr), - }; - - // https://github.com/facebook/react/blob/d1afcb43fd506297109c32ff462f6f659f9110ae/packages/react-refresh/src/ReactFreshBabelPlugin.js#L42 - pub fn isComponentishName(id: []const u8) bool { - if (id.len == 0) return false; - return switch (id[0]) { - 'A'...'Z' => true, - else => false, - }; - } - - // https://github.com/facebook/react/blob/d1afcb43fd506297109c32ff462f6f659f9110ae/packages/react-refresh/src/ReactFreshBabelPlugin.js#L408 - pub fn isHookName(id: []const u8) bool { - return id.len >= 4 and - strings.hasPrefixComptime(id, "use") and - switch (id[3]) { - 'A'...'Z' => true, - else => false, - }; - } - - pub const built_in_hooks = bun.ComptimeEnumMap(enum { - useState, - useReducer, - useEffect, - useLayoutEffect, - useMemo, - useCallback, - useRef, - useContext, - useImperativeHandle, - useDebugValue, - useId, - useDeferredValue, - useTransition, - useInsertionEffect, - useSyncExternalStore, - useFormStatus, - useFormState, - useActionState, - useOptimistic, - }); - }; - /// use this instead of checking p.source.index /// because when not bundling, p.source.index is `0` inline fn isSourceRuntime(p: *const P) bool { @@ -9399,7 +9263,7 @@ fn NewParser_( } pub fn newSymbol(p: *P, kind: Symbol.Kind, identifier: string) !Ref { - const inner_index = @as(Ref.Int, @truncate(p.symbols.items.len)); + const inner_index: Ref.Int = @truncate(p.symbols.items.len); try p.symbols.append(Symbol{ .kind = kind, .original_name = identifier, @@ -19393,6 +19257,10 @@ fn NewParser_( data.default_name = createDefaultName(p, stmt.loc) catch unreachable; } + if (p.options.features.react_fast_refresh) { + try p.handleReactRefreshRegister(stmts, name, data.default_name.ref.?, .default); + } + if (p.options.features.server_components.wrapsExports()) { data.value = .{ .expr = p.wrapValueForServerComponentReference(p.newExpr(E.Function{ .func = func.func }, stmt.loc), "default") }; } @@ -19542,7 +19410,7 @@ fn NewParser_( } if (p.current_scope == p.module_scope) { - try p.handleReactRefreshRegister(stmts, original_name, name_ref); + try p.handleReactRefreshRegister(stmts, original_name, name_ref, .named); } } @@ -19766,7 +19634,7 @@ fn NewParser_( else => break :try_register, }; const original_name = p.symbols.items[id.innerIndex()].original_name; - try p.handleReactRefreshRegister(stmts, original_name, id); + try p.handleReactRefreshRegister(stmts, original_name, id, .named); } } @@ -23295,7 +23163,7 @@ fn NewParser_( } }; - pub fn handleReactRefreshRegister(p: *P, stmts: *ListManaged(Stmt), original_name: []const u8, ref: Ref) !void { + pub fn handleReactRefreshRegister(p: *P, stmts: *ListManaged(Stmt), original_name: []const u8, ref: Ref, export_kind: enum { named, default }) !void { bun.assert(p.options.features.react_fast_refresh); bun.assert(p.current_scope == p.module_scope); @@ -23310,12 +23178,16 @@ fn NewParser_( .data = try bun.strings.concat(p.allocator, &.{ p.source.path.pretty, ":", - original_name, + switch (export_kind) { + .named => original_name, + .default => "default", + }, }), }, loc), }), }, loc) }, loc)); + p.recordUsage(ref); p.react_refresh.register_used = true; } } @@ -24053,12 +23925,148 @@ const WrapMode = enum { bun_commonjs, }; +/// "Fast Refresh" is React's solution for hot-module-reloading in the context of the UI framework +/// user guide: https://reactnative.dev/docs/fast-refresh (applies to react-dom and native) +/// +/// This depends on performing a couple extra transformations at bundle time, as well as +/// including the `react-refresh` NPM package, which is able to do the heavy lifting, +/// integrating with `react` and `react-dom`. +/// +/// Prior implementations: +/// [1]: https://github.com/facebook/react/blob/main/packages/react-refresh/src/ReactFreshBabelPlugin.js +/// [2]: https://github.com/swc-project/swc/blob/main/crates/swc_ecma_transforms_react/src/refresh/mod.rs +/// +/// Additional reading: +/// [3] https://github.com/facebook/react/issues/16604#issuecomment-528663101 +/// [4] https://github.com/facebook/react/blob/master/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js +/// +/// Instead of a plugin which visits the tree separately, Bun's implementation of fast refresh +/// happens in tandem with the visit pass. The responsibilities of the transform are as follows: +/// +/// 1. For all Components (which is defined as any top-level function/function variable, that is +/// named with a capital letter; see `isComponentishName`), register them to the runtime using +/// `$RefreshReg$(ComponentFunction, "Component");`. Implemented in `p.handleReactRefreshRegister` +/// HOC components are also registered, but only through a special case for `export default` +/// +/// 2. For all functions which call a Hook (a hook is an identifier matching /^use[A-Z]/): +/// a. Outside of the function, create a signature function `const _s = $RefreshSig$();` +/// b. At the start of the function, call `_s()` +/// c. Record all of the hooks called, the variables they are assigned to, and +/// arguments depending on which hook has been used. `useState` and `useReducer`, +/// for example, are special-cased. +/// d. Directly after the function, call `_s(hook, "", forceReset)` +/// - If a user-defined hook is called, the alterate form is used: +/// `_s(hook, "", forceReset, () => [useCustom1, useCustom2])` +/// +/// The upstream transforms do not declare `$RefreshReg$` or `$RefreshSig$`. A typical +/// implementation might look like this, prepending this data to the module start: +/// +/// import * as Refresh from 'react-refresh/runtime'; +/// const $RefreshReg$ = (type, id) => Refresh.register(type, "" + id); +/// const $RefreshSig$ = Refresh.createSignatureFunctionForTransform; +/// +/// Since Bun is a transpiler *and* bundler, we take a slightly different approach. Aside +/// from including the link to the refresh runtime, our notation of $RefreshReg$ is just +/// pointing at `Refresh.register`, which means when we call it, the second argument has +/// to be a string containing the filepath, not just the component name. +const ReactRefresh = struct { + // Set if this JSX/TSX file uses the refresh runtime. If so, + // we must insert an import statement to it. + register_used: bool = false, + signature_used: bool = false, + + /// $RefreshReg$ is called on all top-level variables that are + /// components, as well as HOCs found in the `export default` clause. + register_ref: Ref = Ref.None, + + /// $RefreshSig$ is called to create a signature function, which is + /// used by the refresh runtime to perform smart hook tracking. + create_signature_ref: Ref = Ref.None, + + /// If a comment with '@refresh reset' is seen, we will forward a + /// force refresh to the refresh runtime. This lets you reset the + /// state of hooks on an update on a per-component basis. + // TODO: this is never set + force_reset: bool = false, + + /// The last hook that was scanned. This is used when visiting + /// `.s_local`, as we must hash the variable destructure if the + /// hook's result is assigned directly to a local. + last_hook_seen: ?*E.Call = null, + + /// Every function sets up stack memory to hold data related to it's + /// hook tracking. This is a pointer to that ?HookContext, where an + /// inner null means there are no hook calls. + /// + /// The inner value is initialized when the first hook .e_call is + /// visited, where the '_s' symbol is reserved. Additional hook calls + /// append to the `hasher` and `user_hooks` as needed. + /// + /// When a function is done visiting, the stack location is checked, + /// and then it will insert `var _s = ...`, add the `_s()` call at + /// the start of the function, and then add the call to `_s(func, ...)`. + hook_ctx_storage: ?*?HookContext = null, + + pub const HookContext = struct { + hasher: std.hash.Wyhash, + signature_cb: Ref, + user_hooks: std.AutoArrayHashMapUnmanaged(Ref, Expr), + }; + + // https://github.com/facebook/react/blob/d1afcb43fd506297109c32ff462f6f659f9110ae/packages/react-refresh/src/ReactFreshBabelPlugin.js#L42 + pub fn isComponentishName(id: []const u8) bool { + if (id.len == 0) return false; + return switch (id[0]) { + 'A'...'Z' => true, + else => false, + }; + } + + // https://github.com/facebook/react/blob/d1afcb43fd506297109c32ff462f6f659f9110ae/packages/react-refresh/src/ReactFreshBabelPlugin.js#L408 + pub fn isHookName(id: []const u8) bool { + return id.len >= 4 and + strings.hasPrefixComptime(id, "use") and + switch (id[3]) { + 'A'...'Z' => true, + else => false, + }; + } + + pub const built_in_hooks = bun.ComptimeEnumMap(enum { + useState, + useReducer, + useEffect, + useLayoutEffect, + useMemo, + useCallback, + useRef, + useContext, + useImperativeHandle, + useDebugValue, + useId, + useDeferredValue, + useTransition, + useInsertionEffect, + useSyncExternalStore, + useFormStatus, + useFormState, + useActionState, + useOptimistic, + }); +}; + pub const ConvertESMExportsForHmr = struct { last_part: *js_ast.Part, - imports_seen: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, + imports_seen: bun.StringArrayHashMapUnmanaged(ImportRef) = .{}, + export_star_props: std.ArrayListUnmanaged(G.Property) = .{}, export_props: std.ArrayListUnmanaged(G.Property) = .{}, stmts: std.ArrayListUnmanaged(Stmt) = .{}, + const ImportRef = struct { + /// Index into ConvertESMExportsForHmr.stmts + stmt_index: u32, + }; + fn convertStmt(ctx: *ConvertESMExportsForHmr, p: anytype, stmt: Stmt) !void { const new_stmt = switch (stmt.data) { else => stmt, @@ -24114,7 +24122,26 @@ pub const ConvertESMExportsForHmr = struct { break :stmt stmt; }, .s_export_default => |st| stmt: { - // Simple case: we can move this to the default property of the exports object + // When React Fast Refresh needs to tag the default export, the statement + // cannot be moved, since a local reference is required. + if (p.options.features.react_fast_refresh and + st.value == .stmt and st.value.stmt.data == .s_function) + fast_refresh_edge_case: { + const symbol = st.value.stmt.data.s_function.func.name orelse + break :fast_refresh_edge_case; + const name = p.symbols.items[symbol.ref.?.inner_index].original_name; + if (ReactRefresh.isComponentishName(name)) { + // Lower to a function statement, and reference the function in the export list. + try ctx.export_props.append(p.allocator, .{ + .key = Expr.init(E.String, .{ .data = "default" }, stmt.loc), + .value = Expr.initIdentifier(symbol.ref.?, stmt.loc), + }); + break :stmt st.value.stmt; + } + // All other functions can be properly moved. + } + + // Try to move the export default expression to the end. if (st.canBeMoved()) { try ctx.export_props.append(p.allocator, .{ .key = Expr.init(E.String, .{ .data = "default" }, stmt.loc), @@ -24124,7 +24151,7 @@ pub const ConvertESMExportsForHmr = struct { return; } - // Otherwise, we need a temporary + // Otherwise, a new symbol is needed const temp_id = p.generateTempRef("default_export"); try ctx.last_part.declared_symbols.append(p.allocator, .{ .ref = temp_id, .is_top_level = true }); try ctx.last_part.symbol_uses.putNoClobber(p.allocator, temp_id, .{ .count_estimate = 1 }); @@ -24185,13 +24212,22 @@ pub const ConvertESMExportsForHmr = struct { return; // do not emit a statement here }, - .s_export_from => |st| stmt: { + .s_export_from => |st| { + const namespace_ref = try ctx.deduplicatedImport( + p, + st.import_record_index, + st.namespace_ref, + st.items, + stmt.loc, + null, + stmt.loc, + ); for (st.items) |*item| { const ref = item.name.ref.?; const symbol = &p.symbols.items[ref.innerIndex()]; if (symbol.namespace_alias == null) { symbol.namespace_alias = .{ - .namespace_ref = st.namespace_ref, + .namespace_ref = namespace_ref, .alias = item.original_name, .import_record_index = st.import_record_index, }; @@ -24207,34 +24243,108 @@ pub const ConvertESMExportsForHmr = struct { item.alias = item.original_name; item.original_name = alias; } - - const gop = try ctx.imports_seen.getOrPut(p.allocator, st.import_record_index); - if (gop.found_existing) return; - break :stmt Stmt.alloc(S.Import, .{ - .import_record_index = st.import_record_index, - .is_single_line = true, - .default_name = null, - .items = st.items, - .namespace_ref = st.namespace_ref, - .star_name_loc = null, - }, stmt.loc); + return; }, - .s_export_star => { - bun.todoPanic(@src(), "hot-module-reloading instrumentation for 'export * from'", .{}); + .s_export_star => |st| { + const namespace_ref = try ctx.deduplicatedImport( + p, + st.import_record_index, + st.namespace_ref, + &.{}, + stmt.loc, + null, + stmt.loc, + ); + try ctx.export_star_props.append(p.allocator, .{ + .kind = .spread, + .value = Expr.initIdentifier(namespace_ref, stmt.loc), + }); + return; }, // De-duplicate import statements. It is okay to disregard // named/default imports here as we always rewrite them as - // full qualified property accesses (need to so live-bindings) - .s_import => |st| stmt: { - const gop = try ctx.imports_seen.getOrPut(p.allocator, st.import_record_index); - if (gop.found_existing) return; - break :stmt stmt; + // full qualified property accesses (needed for live-bindings) + .s_import => |st| { + _ = try ctx.deduplicatedImport( + p, + st.import_record_index, + st.namespace_ref, + st.items, + st.star_name_loc, + st.default_name, + stmt.loc, + ); + return; }, }; try ctx.stmts.append(p.allocator, new_stmt); } + /// Deduplicates imports, returning a previously used Ref if present. + fn deduplicatedImport( + ctx: *ConvertESMExportsForHmr, + p: anytype, + import_record_index: u32, + namespace_ref: Ref, + items: []js_ast.ClauseItem, + star_name_loc: ?logger.Loc, + default_name: ?js_ast.LocRef, + loc: logger.Loc, + ) !Ref { + const ir = &p.import_records.items[import_record_index]; + const gop = try ctx.imports_seen.getOrPut(p.allocator, ir.path.text); + if (gop.found_existing) { + // Disable this one since an older record is getting used. It isn't + // practical to delete this import record entry since an import or + // require expression can exist. + ir.is_unused = true; + + const stmt = ctx.stmts.items[gop.value_ptr.stmt_index].data.s_import; + if (items.len > 0) { + if (stmt.items.len == 0) { + stmt.items = items; + } else { + stmt.items = try std.mem.concat(p.allocator, js_ast.ClauseItem, &.{ stmt.items, items }); + } + } + if (namespace_ref.isValid()) { + if (!stmt.namespace_ref.isValid()) { + stmt.namespace_ref = namespace_ref; + return namespace_ref; + } else { + // Erase this namespace ref, but since it may be used in + // existing AST trees, a link must be established. + const symbol = &p.symbols.items[namespace_ref.innerIndex()]; + symbol.use_count_estimate = 0; + symbol.link = stmt.namespace_ref; + if (@hasField(@typeInfo(@TypeOf(p)).pointer.child, "symbol_uses")) { + _ = p.symbol_uses.swapRemove(namespace_ref); + } + } + } + if (stmt.star_name_loc == null) if (star_name_loc) |stl| { + stmt.star_name_loc = stl; + }; + if (stmt.default_name == null) if (default_name) |dn| { + stmt.default_name = dn; + }; + return stmt.namespace_ref; + } + + try ctx.stmts.append(p.allocator, Stmt.alloc(S.Import, .{ + .import_record_index = import_record_index, + .is_single_line = true, + .default_name = default_name, + .items = items, + .namespace_ref = namespace_ref, + .star_name_loc = star_name_loc, + }, loc)); + + gop.value_ptr.* = .{ .stmt_index = @intCast(ctx.stmts.items.len - 1) }; + return namespace_ref; + } + fn visitBindingToExport( ctx: *ConvertESMExportsForHmr, p: anytype, @@ -24316,6 +24426,18 @@ pub const ConvertESMExportsForHmr = struct { } pub fn finalize(ctx: *ConvertESMExportsForHmr, p: anytype, all_parts: []js_ast.Part) !void { + if (ctx.export_star_props.items.len > 0) { + if (ctx.export_props.items.len == 0) { + ctx.export_props = ctx.export_star_props; + } else { + const export_star_len = ctx.export_star_props.items.len; + try ctx.export_props.ensureUnusedCapacity(p.allocator, export_star_len); + const len = ctx.export_props.items.len; + ctx.export_props.items.len += export_star_len; + bun.copy(G.Property, ctx.export_props.items[export_star_len..], ctx.export_props.items[0..len]); + @memcpy(ctx.export_props.items[0..export_star_len], ctx.export_star_props.items); + } + } if (ctx.export_props.items.len > 0) { try ctx.stmts.append(p.allocator, Stmt.alloc(S.SExpr, .{ .value = Expr.assign( @@ -24335,6 +24457,8 @@ pub const ConvertESMExportsForHmr = struct { try ctx.last_part.declared_symbols.append(p.allocator, .{ .ref = p.module_ref, .is_top_level = true }); } + // TODO: emit a marker for HMR runtime to know the non-star export fields. + // TODO: this is a tiny mess. it is honestly trying to hard to merge all parts into one for (all_parts[0 .. all_parts.len - 1]) |*part| { try ctx.last_part.declared_symbols.appendList(p.allocator, part.declared_symbols); diff --git a/src/js_printer.zig b/src/js_printer.zig index 42bfb573a6a9fd..a9fcaa86406492 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -450,6 +450,7 @@ pub const Options = struct { module_hash: u32 = 0, source_path: ?fs.Path = null, allocator: std.mem.Allocator = default_allocator, + source_map_allocator: ?std.mem.Allocator = null, source_map_handler: ?SourceMapHandler = null, source_map_builder: ?*bun.sourcemap.Chunk.Builder = null, css_import_behavior: Api.CssInJsBehavior = Api.CssInJsBehavior.facade, @@ -1856,12 +1857,17 @@ fn NewPrinter( p.printSpaceBeforeIdentifier(); // Allow it to fail at runtime, if it should - p.print("import("); - p.printImportRecordPath(record); + if (module_type != .internal_bake_dev) { + p.print("import("); + p.printImportRecordPath(record); + } else { + p.printSymbol(p.options.commonjs_module_ref); + p.print(".dynamicImport("); + const path = record.path; + p.printStringLiteralUTF8(path.pretty, false); + } if (!import_options.isMissing()) { - // since we previously stripped type, it is a breaking change to - // enable this for non-bun platforms p.printWhitespacer(ws(", ")); p.printExpr(import_options, .comma, .{}); } @@ -2016,6 +2022,7 @@ fn NewPrinter( switch (expr.data) { .e_missing => {}, .e_undefined => { + p.addSourceMapping(expr.loc); p.printUndefined(expr.loc, level); }, .e_super => { @@ -2356,8 +2363,6 @@ fn NewPrinter( p.printExpr(e.expr, .comma, ExprFlag.None()); if (!e.options.isMissing()) { - // since we previously stripped type, it is a breaking change to - // enable this for non-bun platforms p.printWhitespacer(ws(", ")); p.printExpr(e.options, .comma, .{}); } @@ -2558,8 +2563,8 @@ fn NewPrinter( } p.printSpaceBeforeIdentifier(); + p.addSourceMapping(expr.loc); if (e.func.flags.contains(.is_async)) { - p.addSourceMapping(expr.loc); p.print("async "); } p.print("function"); @@ -2890,7 +2895,6 @@ fn NewPrinter( // } if (!didPrint) { - // assert(p.options.module_type != .internal_bake_dev); p.printSpaceBeforeIdentifier(); p.addSourceMapping(expr.loc); p.printSymbol(e.ref); @@ -2950,6 +2954,7 @@ fn NewPrinter( if (entry.is_keyword) { p.printSpaceBeforeIdentifier(); + p.addSourceMapping(expr.loc); p.print(entry.text); p.printSpace(); } else { @@ -3624,7 +3629,6 @@ fn NewPrinter( p.prev_stmt_tag = std.meta.activeTag(stmt.data); } - p.addSourceMapping(stmt.loc); switch (stmt.data) { .s_comment => |s| { p.printIndentedComment(s.text); @@ -3632,6 +3636,7 @@ fn NewPrinter( .s_function => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); const name = s.func.name orelse Output.panic("Internal error: expected func to have a name ref\n{any}", .{s}); const nameRef = name.ref orelse Output.panic("Internal error: expected func to have a name\n{any}", .{s}); @@ -3647,9 +3652,10 @@ fn NewPrinter( if (s.func.flags.contains(.is_generator)) { p.print("*"); p.printSpace(); + } else { + p.printSpaceBeforeIdentifier(); } - p.printSpaceBeforeIdentifier(); p.addSourceMapping(name.loc); p.printSymbol(nameRef); p.printFunc(s.func); @@ -3679,6 +3685,7 @@ fn NewPrinter( p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); const nameRef = s.class.class_name.?.ref.?; if (s.is_export) { if (!rewrite_esm_to_cjs) { @@ -3709,12 +3716,14 @@ fn NewPrinter( if (p.prev_stmt_tag == .s_empty and p.options.indent.count == 0) return; p.printIndent(); + p.addSourceMapping(stmt.loc); p.print(";"); p.printNewline(); }, .s_export_default => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("export default "); switch (s.value) { @@ -3781,6 +3790,7 @@ fn NewPrinter( } p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); if (s.alias != null) p.printWhitespacer(comptime ws("export *").append(" as ")) @@ -3800,6 +3810,7 @@ fn NewPrinter( if (rewrite_esm_to_cjs) { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); switch (s.items.len) { 0 => {}, @@ -3860,6 +3871,7 @@ fn NewPrinter( p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("export"); p.printSpace(); @@ -3959,6 +3971,7 @@ fn NewPrinter( .s_export_from => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); const import_record = p.importRecord(s.import_record_index); @@ -3998,6 +4011,9 @@ fn NewPrinter( p.printSemicolonAfterStatement(); }, .s_local => |s| { + p.printIndent(); + p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); switch (s.kind) { .k_const => { p.printDeclStmt(s.is_export, "const", s.decls.slice()); @@ -4018,11 +4034,12 @@ fn NewPrinter( }, .s_if => |s| { p.printIndent(); - p.printIf(s); + p.printIf(s, stmt.loc); }, .s_do_while => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("do"); switch (s.body.data) { .s_block => { @@ -4050,6 +4067,7 @@ fn NewPrinter( .s_for_in => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("for"); p.printSpace(); p.print("("); @@ -4065,6 +4083,7 @@ fn NewPrinter( .s_for_of => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("for"); if (s.is_await) { p.print(" await"); @@ -4084,6 +4103,7 @@ fn NewPrinter( .s_while => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("while"); p.printSpace(); p.print("("); @@ -4094,6 +4114,7 @@ fn NewPrinter( .s_with => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("with"); p.printSpace(); p.print("("); @@ -4103,10 +4124,10 @@ fn NewPrinter( }, .s_label => |s| { if (!p.options.minify_whitespace and p.options.indent.count > 0) { - p.addSourceMapping(stmt.loc); p.printIndent(); } p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.printSymbol(s.name.ref orelse Output.panic("Internal error: expected label to have a name {any}", .{s})); p.print(":"); p.printBody(s.stmt); @@ -4114,12 +4135,14 @@ fn NewPrinter( .s_try => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("try"); p.printSpace(); p.printBlock(s.body_loc, s.body, null); if (s.catch_) |catch_| { p.printSpace(); + p.addSourceMapping(catch_.loc); p.print("catch"); if (catch_.binding) |binding| { p.printSpace(); @@ -4128,7 +4151,7 @@ fn NewPrinter( p.print(")"); } p.printSpace(); - p.printBlock(catch_.loc, catch_.body, null); + p.printBlock(catch_.body_loc, catch_.body, null); } if (s.finally) |finally| { @@ -4143,6 +4166,7 @@ fn NewPrinter( .s_for => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("for"); p.printSpace(); p.print("("); @@ -4170,6 +4194,7 @@ fn NewPrinter( .s_switch => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("switch"); p.printSpace(); p.print("("); @@ -4232,7 +4257,7 @@ fn NewPrinter( .css => { switch (p.options.css_import_behavior) { .facade => { - + p.addSourceMapping(stmt.loc); // This comment exists to let tooling authors know which files CSS originated from // To parse this, you just look for a line that starts with //@import url(" p.print("//@import url(\""); @@ -4252,6 +4277,7 @@ fn NewPrinter( }, .auto_onimportcss, .facade_onimportcss => { + p.addSourceMapping(stmt.loc); p.print("globalThis.document?.dispatchEvent(new CustomEvent(\"onimportcss\", {detail: "); p.printStringLiteralUTF8(record.path.text, false); p.print("}));\n"); @@ -4268,6 +4294,7 @@ fn NewPrinter( return; }, .import_path => { + p.addSourceMapping(stmt.loc); if (s.default_name) |name| { p.print("var "); p.printSymbol(name.ref.?); @@ -4289,6 +4316,8 @@ fn NewPrinter( .napi_module => { if (comptime is_bun_platform) { p.printIndent(); + p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("var "); p.printSymbol(s.namespace_ref); p.@"print = "(); @@ -4305,6 +4334,7 @@ fn NewPrinter( p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); if (comptime is_bun_platform) { switch (record.tag) { @@ -4496,6 +4526,7 @@ fn NewPrinter( .s_debugger => { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("debugger"); p.printSemicolonAfterStatement(); }, @@ -4505,12 +4536,14 @@ fn NewPrinter( p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.printStringLiteralUTF8(s.value, false); p.printSemicolonAfterStatement(); }, .s_break => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("break"); if (s.label) |label| { p.print(" "); @@ -4522,6 +4555,7 @@ fn NewPrinter( .s_continue => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("continue"); if (s.label) |label| { @@ -4533,6 +4567,7 @@ fn NewPrinter( .s_return => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("return"); if (s.value) |value| { @@ -4544,6 +4579,7 @@ fn NewPrinter( .s_throw => |s| { p.printIndent(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(stmt.loc); p.print("throw"); p.printSpace(); p.printExpr(s.value, .lowest, ExprFlag.None()); @@ -4551,7 +4587,6 @@ fn NewPrinter( }, .s_expr => |s| { if (!p.options.minify_whitespace and p.options.indent.count > 0) { - p.addSourceMapping(stmt.loc); p.printIndent(); } @@ -4784,8 +4819,9 @@ fn NewPrinter( }, } } - pub fn printIf(p: *Printer, s: *const S.If) void { + pub fn printIf(p: *Printer, s: *const S.If, loc: logger.Loc) void { p.printSpaceBeforeIdentifier(); + p.addSourceMapping(loc); p.print("if"); p.printSpace(); p.print("("); @@ -4838,6 +4874,7 @@ fn NewPrinter( if (s.no) |no_block| { p.printSemicolonIfNeeded(); p.printSpaceBeforeIdentifier(); + p.addSourceMapping(no_block.loc); p.print("else"); switch (no_block.data) { @@ -4847,7 +4884,7 @@ fn NewPrinter( p.printNewline(); }, .s_if => { - p.printIf(no_block.data.s_if); + p.printIf(no_block.data.s_if, no_block.loc); }, else => { p.printNewline(); @@ -4934,9 +4971,6 @@ fn NewPrinter( } pub fn printDeclStmt(p: *Printer, is_export: bool, comptime keyword: string, decls: []G.Decl) void { - p.printIndent(); - p.printSpaceBeforeIdentifier(); - if (!rewrite_esm_to_cjs and is_export) { p.print("export "); } @@ -5637,8 +5671,8 @@ pub fn getSourceMapBuilder( return undefined; return .{ - .source_map = SourceMap.Chunk.Builder.SourceMapper.init( - opts.allocator, + .source_map = .init( + opts.source_map_allocator orelse opts.allocator, is_bun_platform and generate_source_map == .lazy, ), .cover_lines_without_mappings = true, @@ -5646,15 +5680,14 @@ pub fn getSourceMapBuilder( .prepend_count = is_bun_platform and generate_source_map == .lazy, .line_offset_tables = opts.line_offset_tables orelse brk: { if (generate_source_map == .lazy) break :brk SourceMap.LineOffsetTable.generate( - opts.allocator, + opts.source_map_allocator orelse opts.allocator, source.contents, @as( i32, @intCast(tree.approximate_newline_count), ), ); - - break :brk SourceMap.LineOffsetTable.List{}; + break :brk .empty; }, }; } @@ -5899,7 +5932,7 @@ pub fn print( pub fn printWithWriter( comptime Writer: type, - _writer: Writer, + writer: Writer, target: options.Target, ast: Ast, source: *const logger.Source, @@ -5912,7 +5945,7 @@ pub fn printWithWriter( return switch (target.isBun()) { inline else => |is_bun| printWithWriterAndPlatform( Writer, - _writer, + writer, is_bun, ast, source, @@ -5928,7 +5961,7 @@ pub fn printWithWriter( /// The real one pub fn printWithWriterAndPlatform( comptime Writer: type, - _writer: Writer, + writer: Writer, comptime is_bun_platform: bool, ast: Ast, source: *const logger.Source, @@ -5951,7 +5984,6 @@ pub fn printWithWriterAndPlatform( false, generate_source_maps, ); - const writer = _writer; var printer = PrinterType.init( writer, import_records, @@ -5964,7 +5996,7 @@ pub fn printWithWriterAndPlatform( defer printer.binary_expression_stack.clearAndFree(); defer printer.temporary_bindings.deinit(bun.default_allocator); - defer _writer.* = printer.writer.*; + defer writer.* = printer.writer.*; defer { imported_module_ids_list = printer.imported_module_ids; } @@ -5972,6 +6004,9 @@ pub fn printWithWriterAndPlatform( if (opts.module_type == .internal_bake_dev) { printer.indent(); printer.printIndent(); + if (!ast.top_level_await_keyword.isEmpty()) { + printer.print("async "); + } printer.printStringLiteralUTF8(source.path.pretty, false); const func = parts[0].stmts[0].data.s_expr.value.data.e_function.func; if (!(func.body.stmts.len == 1 and func.body.stmts[0].data == .s_lazy_export)) { diff --git a/src/node-fallbacks/bun.lock b/src/node-fallbacks/bun.lock index 26187aaf6f5de7..1730d5da2dc3be 100644 --- a/src/node-fallbacks/bun.lock +++ b/src/node-fallbacks/bun.lock @@ -19,6 +19,7 @@ "process": "^0.11.10", "punycode": "^2.1.1", "querystring-es3": "^1.0.0-0", + "react-refresh": "^0.16.0", "readable-stream": "^4.1.0", "stream-http": "^3.2.0", "string_decoder": "^1.3.0", @@ -31,7 +32,7 @@ }, }, "packages": { - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.14.54", "", { "os": "linux", "cpu": "none" }, "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.14.54", "", { "os":"linux", "cpu":"none" }, "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw=="], "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], @@ -95,45 +96,45 @@ "esbuild": ["esbuild@0.14.54", "", { "dependencies": { "@esbuild/linux-loong64": "0.14.54", "esbuild-android-64": "0.14.54", "esbuild-android-arm64": "0.14.54", "esbuild-darwin-64": "0.14.54", "esbuild-darwin-arm64": "0.14.54", "esbuild-freebsd-64": "0.14.54", "esbuild-freebsd-arm64": "0.14.54", "esbuild-linux-32": "0.14.54", "esbuild-linux-64": "0.14.54", "esbuild-linux-arm": "0.14.54", "esbuild-linux-arm64": "0.14.54", "esbuild-linux-mips64le": "0.14.54", "esbuild-linux-ppc64le": "0.14.54", "esbuild-linux-riscv64": "0.14.54", "esbuild-linux-s390x": "0.14.54", "esbuild-netbsd-64": "0.14.54", "esbuild-openbsd-64": "0.14.54", "esbuild-sunos-64": "0.14.54", "esbuild-windows-32": "0.14.54", "esbuild-windows-64": "0.14.54", "esbuild-windows-arm64": "0.14.54" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA=="], - "esbuild-android-64": ["esbuild-android-64@0.14.54", "", { "os": "android", "cpu": "x64" }, "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ=="], + "esbuild-android-64": ["esbuild-android-64@0.14.54", "", { "os":"android", "cpu":"x64" }, "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ=="], - "esbuild-android-arm64": ["esbuild-android-arm64@0.14.54", "", { "os": "android", "cpu": "arm64" }, "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg=="], + "esbuild-android-arm64": ["esbuild-android-arm64@0.14.54", "", { "os":"android", "cpu":"arm64" }, "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg=="], - "esbuild-darwin-64": ["esbuild-darwin-64@0.14.54", "", { "os": "darwin", "cpu": "x64" }, "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug=="], + "esbuild-darwin-64": ["esbuild-darwin-64@0.14.54", "", { "os":"darwin", "cpu":"x64" }, "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug=="], - "esbuild-darwin-arm64": ["esbuild-darwin-arm64@0.14.54", "", { "os": "darwin", "cpu": "arm64" }, "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw=="], + "esbuild-darwin-arm64": ["esbuild-darwin-arm64@0.14.54", "", { "os":"darwin", "cpu":"arm64" }, "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw=="], - "esbuild-freebsd-64": ["esbuild-freebsd-64@0.14.54", "", { "os": "freebsd", "cpu": "x64" }, "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg=="], + "esbuild-freebsd-64": ["esbuild-freebsd-64@0.14.54", "", { "os":"freebsd", "cpu":"x64" }, "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg=="], - "esbuild-freebsd-arm64": ["esbuild-freebsd-arm64@0.14.54", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q=="], + "esbuild-freebsd-arm64": ["esbuild-freebsd-arm64@0.14.54", "", { "os":"freebsd", "cpu":"arm64" }, "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q=="], - "esbuild-linux-32": ["esbuild-linux-32@0.14.54", "", { "os": "linux", "cpu": "ia32" }, "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw=="], + "esbuild-linux-32": ["esbuild-linux-32@0.14.54", "", { "os":"linux", "cpu":"ia32" }, "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw=="], - "esbuild-linux-64": ["esbuild-linux-64@0.14.54", "", { "os": "linux", "cpu": "x64" }, "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg=="], + "esbuild-linux-64": ["esbuild-linux-64@0.14.54", "", { "os":"linux", "cpu":"x64" }, "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg=="], - "esbuild-linux-arm": ["esbuild-linux-arm@0.14.54", "", { "os": "linux", "cpu": "arm" }, "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw=="], + "esbuild-linux-arm": ["esbuild-linux-arm@0.14.54", "", { "os":"linux", "cpu":"arm" }, "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw=="], - "esbuild-linux-arm64": ["esbuild-linux-arm64@0.14.54", "", { "os": "linux", "cpu": "arm64" }, "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig=="], + "esbuild-linux-arm64": ["esbuild-linux-arm64@0.14.54", "", { "os":"linux", "cpu":"arm64" }, "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig=="], - "esbuild-linux-mips64le": ["esbuild-linux-mips64le@0.14.54", "", { "os": "linux", "cpu": "none" }, "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw=="], + "esbuild-linux-mips64le": ["esbuild-linux-mips64le@0.14.54", "", { "os":"linux", "cpu":"none" }, "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw=="], - "esbuild-linux-ppc64le": ["esbuild-linux-ppc64le@0.14.54", "", { "os": "linux", "cpu": "ppc64" }, "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ=="], + "esbuild-linux-ppc64le": ["esbuild-linux-ppc64le@0.14.54", "", { "os":"linux", "cpu":"ppc64" }, "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ=="], - "esbuild-linux-riscv64": ["esbuild-linux-riscv64@0.14.54", "", { "os": "linux", "cpu": "none" }, "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg=="], + "esbuild-linux-riscv64": ["esbuild-linux-riscv64@0.14.54", "", { "os":"linux", "cpu":"none" }, "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg=="], - "esbuild-linux-s390x": ["esbuild-linux-s390x@0.14.54", "", { "os": "linux", "cpu": "s390x" }, "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA=="], + "esbuild-linux-s390x": ["esbuild-linux-s390x@0.14.54", "", { "os":"linux", "cpu":"s390x" }, "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA=="], - "esbuild-netbsd-64": ["esbuild-netbsd-64@0.14.54", "", { "os": "none", "cpu": "x64" }, "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w=="], + "esbuild-netbsd-64": ["esbuild-netbsd-64@0.14.54", "", { "os":"none", "cpu":"x64" }, "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w=="], - "esbuild-openbsd-64": ["esbuild-openbsd-64@0.14.54", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw=="], + "esbuild-openbsd-64": ["esbuild-openbsd-64@0.14.54", "", { "os":"openbsd", "cpu":"x64" }, "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw=="], - "esbuild-sunos-64": ["esbuild-sunos-64@0.14.54", "", { "os": "sunos", "cpu": "x64" }, "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw=="], + "esbuild-sunos-64": ["esbuild-sunos-64@0.14.54", "", { "os":"sunos", "cpu":"x64" }, "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw=="], - "esbuild-windows-32": ["esbuild-windows-32@0.14.54", "", { "os": "win32", "cpu": "ia32" }, "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w=="], + "esbuild-windows-32": ["esbuild-windows-32@0.14.54", "", { "os":"win32", "cpu":"ia32" }, "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w=="], - "esbuild-windows-64": ["esbuild-windows-64@0.14.54", "", { "os": "win32", "cpu": "x64" }, "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ=="], + "esbuild-windows-64": ["esbuild-windows-64@0.14.54", "", { "os":"win32", "cpu":"x64" }, "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ=="], - "esbuild-windows-arm64": ["esbuild-windows-arm64@0.14.54", "", { "os": "win32", "cpu": "arm64" }, "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg=="], + "esbuild-windows-arm64": ["esbuild-windows-arm64@0.14.54", "", { "os":"win32", "cpu":"arm64" }, "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg=="], "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], @@ -215,6 +216,8 @@ "randomfill": ["randomfill@1.0.4", "", { "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" } }, "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw=="], + "react-refresh": ["react-refresh@0.16.0", "", {}, "sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A=="], + "readable-stream": ["readable-stream@4.3.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10" } }, "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ=="], "ripemd160": ["ripemd160@2.0.2", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA=="], diff --git a/src/node-fallbacks/package.json b/src/node-fallbacks/package.json index 4c54b4c438df99..99dedbb47bab27 100644 --- a/src/node-fallbacks/package.json +++ b/src/node-fallbacks/package.json @@ -6,7 +6,8 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build-gen": "bash -c 'esbuild --bundle *.js --outdir=bun --format=esm --platform=browser --external:buffer --external:stream --external:util --external:util/ --external:assert'", - "build": "bash -c 'esbuild --bundle *.js --outdir=out --format=esm --minify --platform=browser'" + "build": "bash -c 'esbuild --bundle *.js --outdir=out --format=esm --minify --platform=browser'", + "build-react-refresh": "NODE_ENV=development bun build --target=browser --external=* --format=cjs --outfile=out/react-refresh.js ./node_modules/react-refresh/cjs/react-refresh-runtime.development.js --define=process.env.NODE_ENV=development --minify" }, "author": "", "license": "ISC", @@ -26,6 +27,7 @@ "process": "^0.11.10", "punycode": "^2.1.1", "querystring-es3": "^1.0.0-0", + "react-refresh": "^0.16.0", "readable-stream": "^4.1.0", "stream-http": "^3.2.0", "string_decoder": "^1.3.0", diff --git a/src/renamer.zig b/src/renamer.zig index c41e4ca66c2f88..ac4c38cda3f03b 100644 --- a/src/renamer.zig +++ b/src/renamer.zig @@ -547,7 +547,7 @@ pub const NumberRenamer = struct { .fixed_buffer_allocator = undefined, }; renamer.name_temp_allocator = renamer.name_stack_fallback.get(); - renamer.number_scope_pool = bun.HiveArray(NumberScope, 128).Fallback.init(renamer.arena.allocator()); + renamer.number_scope_pool = .init(renamer.arena.allocator()); renamer.root.name_counts = root_names; if (comptime Environment.allow_assert and !Environment.isWindows) { if (std.posix.getenv("BUN_DUMP_SYMBOLS") != null) @@ -597,7 +597,7 @@ pub const NumberRenamer = struct { std.sort.pdq(u32, sorted.items, {}, std.sort.asc(u32)); for (sorted.items) |inner_index| { - r.assignName(s, Ref.init(@as(Ref.Int, @intCast(inner_index)), source_index, false)); + r.assignName(s, Ref.init(@intCast(inner_index), source_index, false)); } } diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig index c1f9b3e9959ed8..66038e5d37e721 100644 --- a/src/resolver/resolve_path.zig +++ b/src/resolver/resolve_path.zig @@ -86,8 +86,7 @@ pub fn isParentOrEqual(parent_: []const u8, child: []const u8) ParentEqual { return .unrelated; } -pub fn getIfExistsLongestCommonPathGeneric(input: []const []const u8, comptime _platform: Platform) ?[]const u8 { - const platform = comptime _platform.resolve(); +pub fn getIfExistsLongestCommonPathGeneric(input: []const []const u8, comptime platform: Platform) ?[]const u8 { const separator = comptime platform.separator(); const isPathSeparator = comptime platform.getSeparatorFunc(); @@ -178,8 +177,7 @@ pub fn getIfExistsLongestCommonPathGeneric(input: []const []const u8, comptime _ // TODO: is it faster to determine longest_common_separator in the while loop // or as an extra step at the end? // only boether to check if this function appears in benchmarking -pub fn longestCommonPathGeneric(input: []const []const u8, comptime _platform: Platform) []const u8 { - const platform = comptime _platform.resolve(); +pub fn longestCommonPathGeneric(input: []const []const u8, comptime platform: Platform) []const u8 { const separator = comptime platform.separator(); const isPathSeparator = comptime platform.getSeparatorFunc(); @@ -318,9 +316,8 @@ pub fn relativeToCommonPath( normalized_to_: []const u8, buf: []u8, comptime always_copy: bool, - comptime _platform: Platform, + comptime platform: Platform, ) []const u8 { - const platform = comptime _platform.resolve(); var normalized_from = normalized_from_; var normalized_to = normalized_to_; const win_root_len = if (platform == .windows) k: { @@ -463,8 +460,7 @@ pub fn relativeToCommonPath( return out_slice; } -pub fn relativeNormalizedBuf(buf: []u8, from: []const u8, to: []const u8, comptime _platform: Platform, comptime always_copy: bool) []const u8 { - const platform = comptime _platform.resolve(); +pub fn relativeNormalizedBuf(buf: []u8, from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { if ((if (platform == .windows) strings.eqlCaseInsensitiveASCII(from, to, true) else @@ -480,11 +476,11 @@ pub fn relativeNormalizedBuf(buf: []u8, from: []const u8, to: []const u8, compti } pub fn relativeNormalized(from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { - return relativeNormalizedBuf(&relative_to_common_path_buf, from, to, comptime platform.resolve(), always_copy); + return relativeNormalizedBuf(&relative_to_common_path_buf, from, to, platform, always_copy); } pub fn dirname(str: []const u8, comptime platform: Platform) []const u8 { - switch (comptime platform.resolve()) { + switch (platform) { .loose => { const separator = lastIndexOfSeparatorLoose(str) orelse return ""; return str[0..separator]; @@ -499,7 +495,7 @@ pub fn dirname(str: []const u8, comptime platform: Platform) []const u8 { const separator = lastIndexOfSeparatorWindows(str) orelse return std.fs.path.diskDesignatorWindows(str); return str[0..separator]; }, - else => @compileError("unreachable"), + else => @compileError("not implemented"), } } @@ -531,8 +527,7 @@ pub fn relativeBufZ(buf: []u8, from: []const u8, to: []const u8) [:0]const u8 { return buf[0..rel.len :0]; } -pub fn relativePlatformBuf(buf: []u8, from: []const u8, to: []const u8, comptime _platform: Platform, comptime always_copy: bool) []const u8 { - const platform = comptime _platform.resolve(); +pub fn relativePlatformBuf(buf: []u8, from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { const normalized_from = if (platform.isAbsolute(from)) brk: { if (platform == .loose and bun.Environment.isWindows) { // we want to invoke the windows resolution behavior but end up with a @@ -577,11 +572,11 @@ pub fn relativePlatformBuf(buf: []u8, from: []const u8, to: []const u8, comptime } pub fn relativePlatform(from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 { - return relativePlatformBuf(&relative_to_common_path_buf, from, to, comptime platform.resolve(), always_copy); + return relativePlatformBuf(&relative_to_common_path_buf, from, to, platform, always_copy); } pub fn relativeAlloc(allocator: std.mem.Allocator, from: []const u8, to: []const u8) ![]const u8 { - const result = relativePlatform(from, to, Platform.current, false); + const result = relativePlatform(from, to, .auto, false); return try allocator.dupe(u8, result); } @@ -961,20 +956,24 @@ pub fn normalizeStringGenericTZ( } pub const Platform = enum { - auto, loose, windows, posix, nt, + pub const auto: Platform = switch (bun.Environment.os) { + .windows => .windows, + .linux, .mac => .posix, + .wasm => .loose, + }; + pub fn isAbsolute(comptime platform: Platform, path: []const u8) bool { return isAbsoluteT(platform, u8, path); } pub fn isAbsoluteT(comptime platform: Platform, comptime T: type, path: []const T) bool { - if (comptime T != u8 and T != u16) @compileError("Unsupported type given to isAbsoluteT"); - return switch (comptime platform) { - .auto => (comptime platform.resolve()).isAbsoluteT(T, path), + if (T != u8 and T != u16) @compileError("Unsupported type given to isAbsoluteT"); + return switch (platform) { .posix => path.len > 0 and path[0] == '/', .nt, .windows, @@ -986,116 +985,73 @@ pub const Platform = enum { }; } - pub fn separator(comptime platform: Platform) u8 { - return comptime switch (platform) { - .auto => platform.resolve().separator(), + pub inline fn separator(comptime platform: Platform) u8 { + return switch (platform) { .loose, .posix => std.fs.path.sep_posix, .nt, .windows => std.fs.path.sep_windows, }; } - pub fn separatorString(comptime platform: Platform) []const u8 { - return comptime switch (platform) { - .auto => platform.resolve().separatorString(), + pub inline fn separatorString(comptime platform: Platform) []const u8 { + return switch (platform) { .loose, .posix => std.fs.path.sep_str_posix, .nt, .windows => std.fs.path.sep_str_windows, }; } - pub const current: Platform = switch (@import("builtin").target.os.tag) { - .windows => Platform.windows, - else => Platform.posix, - }; - - pub fn getSeparatorFunc(comptime _platform: Platform) IsSeparatorFunc { - switch (comptime _platform.resolve()) { - .auto => @compileError("unreachable"), - .loose => { - return isSepAny; - }, - .nt, .windows => { - return isSepAny; - }, - .posix => { - return isSepPosix; - }, - } + pub fn getSeparatorFunc(comptime platform: Platform) IsSeparatorFunc { + return switch (platform) { + .loose => isSepAny, + .nt, .windows => isSepAny, + .posix => isSepPosix, + }; } - pub fn getSeparatorFuncT(comptime _platform: Platform) IsSeparatorFuncT { - switch (comptime _platform.resolve()) { - .auto => @compileError("unreachable"), - .loose => { - return isSepAnyT; - }, - .nt, .windows => { - return isSepAnyT; - }, - .posix => { - return isSepPosixT; - }, - } + pub fn getSeparatorFuncT(comptime platform: Platform) IsSeparatorFuncT { + return switch (platform) { + .loose => isSepAnyT, + .nt, .windows => isSepAnyT, + .posix => isSepPosixT, + }; } - pub fn getLastSeparatorFunc(comptime _platform: Platform) LastSeparatorFunction { - switch (comptime _platform.resolve()) { - .auto => @compileError("unreachable"), - .loose => { - return lastIndexOfSeparatorLoose; - }, - .nt, .windows => { - return lastIndexOfSeparatorWindows; - }, - .posix => { - return lastIndexOfSeparatorPosix; - }, - } + pub fn getLastSeparatorFunc(comptime platform: Platform) LastSeparatorFunction { + return switch (platform) { + .loose => lastIndexOfSeparatorLoose, + .nt, .windows => lastIndexOfSeparatorWindows, + .posix => lastIndexOfSeparatorPosix, + }; } - pub fn getLastSeparatorFuncT(comptime _platform: Platform) LastSeparatorFunctionT { - switch (comptime _platform.resolve()) { - .auto => @compileError("unreachable"), - .loose => { - return lastIndexOfSeparatorLooseT; - }, - .nt, .windows => { - return lastIndexOfSeparatorWindowsT; - }, - .posix => { - return lastIndexOfSeparatorPosixT; - }, - } + pub fn getLastSeparatorFuncT(comptime platform: Platform) LastSeparatorFunctionT { + return switch (platform) { + .loose => lastIndexOfSeparatorLooseT, + .nt, .windows => lastIndexOfSeparatorWindowsT, + .posix => lastIndexOfSeparatorPosixT, + }; } - pub inline fn isSeparator(comptime _platform: Platform, char: u8) bool { - return isSeparatorT(_platform, u8, char); + pub inline fn isSeparator(comptime platform: Platform, char: u8) bool { + return isSeparatorT(platform, u8, char); } - pub inline fn isSeparatorT(comptime _platform: Platform, comptime T: type, char: T) bool { - switch (comptime _platform.resolve()) { - .auto => @compileError("unreachable"), - .loose => { - return isSepAnyT(T, char); - }, - .nt, .windows => { - return isSepAnyT(T, char); - }, - .posix => { - return isSepPosixT(T, char); - }, - } + pub inline fn isSeparatorT(comptime platform: Platform, comptime T: type, char: T) bool { + return switch (platform) { + .loose => isSepAnyT(T, char), + .nt, .windows => isSepAnyT(T, char), + .posix => isSepPosixT(T, char), + }; } - pub fn trailingSeparator(comptime _platform: Platform) [2]u8 { - return comptime switch (_platform) { - .auto => _platform.resolve().trailingSeparator(), + pub fn trailingSeparator(comptime platform: Platform) [2]u8 { + return switch (platform) { .nt, .windows => ".\\".*, .posix, .loose => "./".*, }; } - pub fn leadingSeparatorIndex(comptime _platform: Platform, path: anytype) ?usize { - switch (comptime _platform.resolve()) { + pub fn leadingSeparatorIndex(comptime platform: Platform, path: anytype) ?usize { + switch (platform) { .nt, .windows => { if (path.len < 1) return null; @@ -1129,66 +1085,51 @@ pub const Platform = enum { return null; } }, - else => { - return leadingSeparatorIndex(.windows, path) orelse leadingSeparatorIndex(.posix, path); - }, + .loose => return leadingSeparatorIndex(.windows, path) orelse + leadingSeparatorIndex(.posix, path), } } - - pub inline fn resolve(comptime _platform: Platform) Platform { - if (comptime _platform == .auto) { - return switch (@import("builtin").target.os.tag) { - .windows => Platform.windows, - - .freestanding, .emscripten, .other => Platform.loose, - - else => Platform.posix, - }; - } - - return _platform; - } }; -pub fn normalizeString(str: []const u8, comptime allow_above_root: bool, comptime _platform: Platform) []u8 { - return normalizeStringBuf(str, &parser_buffer, allow_above_root, _platform, false); +pub fn normalizeString(str: []const u8, comptime allow_above_root: bool, comptime platform: Platform) []u8 { + return normalizeStringBuf(str, &parser_buffer, allow_above_root, platform, false); } -pub fn normalizeStringZ(str: []const u8, comptime allow_above_root: bool, comptime _platform: Platform) [:0]u8 { - const normalized = normalizeStringBuf(str, &parser_buffer, allow_above_root, _platform, false); +pub fn normalizeStringZ(str: []const u8, comptime allow_above_root: bool, comptime platform: Platform) [:0]u8 { + const normalized = normalizeStringBuf(str, &parser_buffer, allow_above_root, platform, false); parser_buffer[normalized.len] = 0; return parser_buffer[0..normalized.len :0]; } -pub fn normalizeBuf(str: []const u8, buf: []u8, comptime _platform: Platform) []u8 { - return normalizeBufT(u8, str, buf, _platform); +pub fn normalizeBuf(str: []const u8, buf: []u8, comptime platform: Platform) []u8 { + return normalizeBufT(u8, str, buf, platform); } -pub fn normalizeBufZ(str: []const u8, buf: []u8, comptime _platform: Platform) [:0]u8 { - const norm = normalizeBufT(u8, str, buf, _platform); +pub fn normalizeBufZ(str: []const u8, buf: []u8, comptime platform: Platform) [:0]u8 { + const norm = normalizeBufT(u8, str, buf, platform); buf[norm.len] = 0; return buf[0..norm.len :0]; } -pub fn normalizeBufT(comptime T: type, str: []const T, buf: []T, comptime _platform: Platform) []T { +pub fn normalizeBufT(comptime T: type, str: []const T, buf: []T, comptime platform: Platform) []T { if (str.len == 0) { buf[0] = '.'; return buf[0..1]; } - const is_absolute = _platform.isAbsoluteT(T, str); + const is_absolute = platform.isAbsoluteT(T, str); - const trailing_separator = _platform.getLastSeparatorFuncT()(T, str) == str.len - 1; + const trailing_separator = platform.getLastSeparatorFuncT()(T, str) == str.len - 1; if (is_absolute and trailing_separator) - return normalizeStringBufT(T, str, buf, true, _platform, true); + return normalizeStringBufT(T, str, buf, true, platform, true); if (is_absolute and !trailing_separator) - return normalizeStringBufT(T, str, buf, true, _platform, false); + return normalizeStringBufT(T, str, buf, true, platform, false); if (!is_absolute and !trailing_separator) - return normalizeStringBufT(T, str, buf, false, _platform, false); + return normalizeStringBufT(T, str, buf, false, platform, false); - return normalizeStringBufT(T, str, buf, false, _platform, true); + return normalizeStringBufT(T, str, buf, false, platform, true); } pub fn normalizeStringBuf( @@ -1209,9 +1150,8 @@ pub fn normalizeStringBufT( comptime platform: Platform, comptime preserve_trailing_slash: bool, ) []T { - switch (comptime platform.resolve()) { - .nt, .auto => @compileError("unreachable"), - + switch (platform) { + .nt => @compileError("not implemented"), .windows => { return normalizeStringWindowsT( T, @@ -1243,18 +1183,18 @@ pub fn normalizeStringBufT( } } -pub fn normalizeStringAlloc(allocator: std.mem.Allocator, str: []const u8, comptime allow_above_root: bool, comptime _platform: Platform) ![]const u8 { - return try allocator.dupe(u8, normalizeString(str, allow_above_root, _platform)); +pub fn normalizeStringAlloc(allocator: std.mem.Allocator, str: []const u8, comptime allow_above_root: bool, comptime platform: Platform) ![]const u8 { + return try allocator.dupe(u8, normalizeString(str, allow_above_root, platform)); } -pub fn joinAbs2(_cwd: []const u8, comptime _platform: Platform, part: anytype, part2: anytype) []const u8 { +pub fn joinAbs2(_cwd: []const u8, comptime platform: Platform, part: anytype, part2: anytype) []const u8 { const parts = [_][]const u8{ part, part2 }; - const slice = joinAbsString(_cwd, &parts, _platform); + const slice = joinAbsString(_cwd, &parts, platform); return slice; } -pub fn joinAbs(cwd: []const u8, comptime _platform: Platform, part: []const u8) []const u8 { - return joinAbsString(cwd, &.{part}, _platform); +pub fn joinAbs(cwd: []const u8, comptime platform: Platform, part: []const u8) []const u8 { + return joinAbsString(cwd, &.{part}, platform); } /// Convert parts of potentially invalid file paths into a single valid filpeath @@ -1262,12 +1202,12 @@ pub fn joinAbs(cwd: []const u8, comptime _platform: Platform, part: []const u8) /// This is the equivalent of path.resolve /// /// Returned path is stored in a temporary buffer. It must be copied if it needs to be stored. -pub fn joinAbsString(_cwd: []const u8, parts: anytype, comptime _platform: Platform) []const u8 { +pub fn joinAbsString(_cwd: []const u8, parts: anytype, comptime platform: Platform) []const u8 { return joinAbsStringBuf( _cwd, &parser_join_input_buffer, parts, - _platform, + platform, ); } @@ -1276,48 +1216,46 @@ pub fn joinAbsString(_cwd: []const u8, parts: anytype, comptime _platform: Platf /// This is the equivalent of path.resolve /// /// Returned path is stored in a temporary buffer. It must be copied if it needs to be stored. -pub fn joinAbsStringZ(_cwd: []const u8, parts: anytype, comptime _platform: Platform) [:0]const u8 { +pub fn joinAbsStringZ(_cwd: []const u8, parts: anytype, comptime platform: Platform) [:0]const u8 { return joinAbsStringBufZ( _cwd, &parser_join_input_buffer, parts, - _platform, + platform, ); } pub threadlocal var join_buf: [4096]u8 = undefined; -pub fn join(_parts: anytype, comptime _platform: Platform) []const u8 { - return joinStringBuf(&join_buf, _parts, _platform); +pub fn join(_parts: anytype, comptime platform: Platform) []const u8 { + return joinStringBuf(&join_buf, _parts, platform); } -pub fn joinZ(_parts: anytype, comptime _platform: Platform) [:0]const u8 { - return joinZBuf(&join_buf, _parts, _platform); +pub fn joinZ(_parts: anytype, comptime platform: Platform) [:0]const u8 { + return joinZBuf(&join_buf, _parts, platform); } -pub fn joinZBuf(buf: []u8, _parts: anytype, comptime _platform: Platform) [:0]const u8 { - const joined = joinStringBuf(buf[0 .. buf.len - 1], _parts, _platform); +pub fn joinZBuf(buf: []u8, _parts: anytype, comptime platform: Platform) [:0]const u8 { + const joined = joinStringBuf(buf[0 .. buf.len - 1], _parts, platform); assert(bun.isSliceInBuffer(joined, buf)); const start_offset = @intFromPtr(joined.ptr) - @intFromPtr(buf.ptr); buf[joined.len + start_offset] = 0; return buf[start_offset..][0..joined.len :0]; } -pub fn joinStringBuf(buf: []u8, parts: anytype, comptime _platform: Platform) []const u8 { - return joinStringBufT(u8, buf, parts, _platform); +pub fn joinStringBuf(buf: []u8, parts: anytype, comptime platform: Platform) []const u8 { + return joinStringBufT(u8, buf, parts, platform); } -pub fn joinStringBufW(buf: []u16, parts: anytype, comptime _platform: Platform) []const u16 { - return joinStringBufT(u16, buf, parts, _platform); +pub fn joinStringBufW(buf: []u16, parts: anytype, comptime platform: Platform) []const u16 { + return joinStringBufT(u16, buf, parts, platform); } -pub fn joinStringBufWZ(buf: []u16, parts: anytype, comptime _platform: Platform) [:0]const u16 { - const joined = joinStringBufT(u16, buf[0 .. buf.len - 1], parts, _platform); +pub fn joinStringBufWZ(buf: []u16, parts: anytype, comptime platform: Platform) [:0]const u16 { + const joined = joinStringBufT(u16, buf[0 .. buf.len - 1], parts, platform); assert(bun.isSliceInBufferT(u16, joined, buf)); const start_offset = @intFromPtr(joined.ptr) / 2 - @intFromPtr(buf.ptr) / 2; buf[joined.len + start_offset] = 0; return buf[start_offset..][0..joined.len :0]; } -pub fn joinStringBufT(comptime T: type, buf: []T, parts: anytype, comptime _platform: Platform) []const T { - const platform = comptime _platform.resolve(); - +pub fn joinStringBufT(comptime T: type, buf: []T, parts: anytype, comptime platform: Platform) []const T { var written: usize = 0; var temp_buf_: [4096]T = undefined; var temp_buf: []T = &temp_buf_; @@ -1367,26 +1305,26 @@ pub fn joinStringBufT(comptime T: type, buf: []T, parts: anytype, comptime _plat return normalizeStringNodeT(T, temp_buf[0..written], buf, platform); } -pub fn joinAbsStringBuf(cwd: []const u8, buf: []u8, _parts: anytype, comptime _platform: Platform) []const u8 { - return _joinAbsStringBuf(false, []const u8, cwd, buf, _parts, _platform); +pub fn joinAbsStringBuf(cwd: []const u8, buf: []u8, _parts: anytype, comptime platform: Platform) []const u8 { + return _joinAbsStringBuf(false, []const u8, cwd, buf, _parts, platform); } -pub fn joinAbsStringBufZ(cwd: []const u8, buf: []u8, _parts: anytype, comptime _platform: Platform) [:0]const u8 { - return _joinAbsStringBuf(true, [:0]const u8, cwd, buf, _parts, _platform); +pub fn joinAbsStringBufZ(cwd: []const u8, buf: []u8, _parts: anytype, comptime platform: Platform) [:0]const u8 { + return _joinAbsStringBuf(true, [:0]const u8, cwd, buf, _parts, platform); } -pub fn joinAbsStringBufZNT(cwd: []const u8, buf: []u8, _parts: anytype, comptime _platform: Platform) [:0]const u8 { - if ((_platform == .auto or _platform == .loose or _platform == .windows) and bun.Environment.isWindows) { +pub fn joinAbsStringBufZNT(cwd: []const u8, buf: []u8, _parts: anytype, comptime platform: Platform) [:0]const u8 { + if ((platform == .auto or platform == .loose or platform == .windows) and bun.Environment.isWindows) { return _joinAbsStringBuf(true, [:0]const u8, cwd, buf, _parts, .nt); } - return _joinAbsStringBuf(true, [:0]const u8, cwd, buf, _parts, _platform); + return _joinAbsStringBuf(true, [:0]const u8, cwd, buf, _parts, platform); } -pub fn joinAbsStringBufZTrailingSlash(cwd: []const u8, buf: []u8, _parts: anytype, comptime _platform: Platform) [:0]const u8 { - const out = _joinAbsStringBuf(true, [:0]const u8, cwd, buf, _parts, _platform); - if (out.len + 2 < buf.len and out.len > 0 and out[out.len - 1] != _platform.separator()) { - buf[out.len] = _platform.separator(); +pub fn joinAbsStringBufZTrailingSlash(cwd: []const u8, buf: []u8, _parts: anytype, comptime platform: Platform) [:0]const u8 { + const out = _joinAbsStringBuf(true, [:0]const u8, cwd, buf, _parts, platform); + if (out.len + 2 < buf.len and out.len > 0 and out[out.len - 1] != platform.separator()) { + buf[out.len] = platform.separator(); buf[out.len + 1] = 0; return buf[0 .. out.len + 1 :0]; } @@ -1394,15 +1332,14 @@ pub fn joinAbsStringBufZTrailingSlash(cwd: []const u8, buf: []u8, _parts: anytyp return out; } -fn _joinAbsStringBuf(comptime is_sentinel: bool, comptime ReturnType: type, _cwd: []const u8, buf: []u8, _parts: anytype, comptime _platform: Platform) ReturnType { - const platform = comptime _platform.resolve(); +fn _joinAbsStringBuf(comptime is_sentinel: bool, comptime ReturnType: type, _cwd: []const u8, buf: []u8, _parts: anytype, comptime platform: Platform) ReturnType { if (platform == .windows or (bun.Environment.os == .windows and platform == .loose)) { return _joinAbsStringBufWindows(is_sentinel, ReturnType, _cwd, buf, _parts); } - if (comptime platform.resolve() == .nt) { + if (platform == .nt) { const end_path = _joinAbsStringBufWindows(is_sentinel, ReturnType, _cwd, buf[4..], _parts); buf[0..4].* = "\\\\?\\".*; if (comptime is_sentinel) { @@ -1744,9 +1681,8 @@ pub fn normalizeStringNodeT( comptime T: type, str: []const T, buf: []T, - comptime _platform: Platform, + comptime platform: Platform, ) []const T { - const platform = comptime _platform.resolve(); if (str.len == 0) { buf[0] = '.'; return buf[0..1]; @@ -1764,7 +1700,7 @@ pub fn normalizeStringNodeT( str, buf_, true, - comptime platform.resolve().separator(), + comptime platform.separator(), comptime platform.getSeparatorFuncT(), false, ) else normalizeStringGenericT( @@ -1772,7 +1708,7 @@ pub fn normalizeStringNodeT( str, buf_, false, - comptime platform.resolve().separator(), + comptime platform.separator(), comptime platform.getSeparatorFuncT(), false, ); @@ -2065,7 +2001,7 @@ export fn ResolvePath__joinAbsStringBufCurrentPlatformBunString( globalObject.bunVM().transpiler.fs.top_level_dir, &join_buf, &.{str.slice()}, - comptime Platform.auto.resolve(), + .auto, ); return bun.String.createUTF8(out_slice); diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index b714d33ace181c..b8ce712bac62c7 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -1629,6 +1629,13 @@ pub const Resolver = struct { /// bust both the named file and a parent directory, because `./hello` can resolve /// to `./hello.js` or `./hello/index.js` pub fn bustDirCacheFromSpecifier(r: *ThisResolver, import_source: []const u8, specifier: []const u8) bool { + if (std.fs.path.isAbsolute(specifier)) { + const dir = bun.path.dirname(specifier, .auto); + const a = r.bustDirCache(dir); + const b = r.bustDirCache(specifier); + return a or b; + } + if (!(bun.strings.startsWith(specifier, "./") or bun.strings.startsWith(specifier, "../"))) return false; if (!std.fs.path.isAbsolute(import_source)) return false; diff --git a/src/sourcemap/sourcemap.zig b/src/sourcemap/sourcemap.zig index 50c37980c19953..bca26ab6acdcfa 100644 --- a/src/sourcemap/sourcemap.zig +++ b/src/sourcemap/sourcemap.zig @@ -1158,14 +1158,11 @@ const vlq_lookup_table: [256]VLQ = brk: { break :brk entries; }; -const vlq_max_in_bytes = 8; +/// Source map VLQ values are limited to i32 +/// Encoding min and max ints are "//////D" and "+/////D", respectively. +/// These are 7 bytes long. This makes the `VLQ` struct 8 bytes. +const vlq_max_in_bytes = 7; pub const VLQ = struct { - // We only need to worry about i32 - // That means the maximum VLQ-encoded value is 8 bytes - // because there are only 4 bits of number inside each VLQ value - // and it expects i32 - // therefore, it can never be more than 32 bits long - // I believe the actual number is 7 bytes long, however we can add an extra byte to be more cautious bytes: [vlq_max_in_bytes]u8, len: u4 = 0, @@ -1602,6 +1599,14 @@ pub const Chunk = struct { /// ignore empty chunks should_ignore: bool = true, + pub const empty: Chunk = .{ + .buffer = MutableString.initEmpty(bun.default_allocator), + .mappings_count = 0, + .end_state = .{}, + .final_generated_column = 0, + .should_ignore = true, + }; + pub fn printSourceMapContents( chunk: Chunk, source: Logger.Source, @@ -1660,13 +1665,14 @@ pub const Chunk = struct { return output; } + // TODO: remove the indirection by having generic functions for SourceMapFormat and NewBuilder. Source maps are always VLQ pub fn SourceMapFormat(comptime Type: type) type { return struct { ctx: Type, const Format = @This(); pub fn init(allocator: std.mem.Allocator, prepend_count: bool) Format { - return Format{ .ctx = Type.init(allocator, prepend_count) }; + return .{ .ctx = Type.init(allocator, prepend_count) }; } pub inline fn appendLineSeparator(this: *Format) anyerror!void { diff --git a/src/sys.zig b/src/sys.zig index b90fbbbe2095c7..1062fcffbcd297 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -4541,3 +4541,12 @@ pub const coreutils_error_map = brk: { break :brk map; }; + +extern fn getRSS(rss: *usize) c_int; +pub fn selfProcessMemoryUsage() ?usize { + var rss: usize = undefined; + if (getRSS(&rss) != 0) { + return null; + } + return rss; +} diff --git a/src/watcher.zig b/src/watcher.zig deleted file mode 100644 index 3f710c64ceb354..00000000000000 --- a/src/watcher.zig +++ /dev/null @@ -1,670 +0,0 @@ -//! Bun's cross-platform filesystem watcher. Runs on its own thread. -const Watcher = @This(); -pub const max_count = 128; - -pub const Event = WatchEvent; -pub const Item = WatchItem; -pub const ItemList = WatchList; -pub const WatchList = std.MultiArrayList(WatchItem); -pub const HashType = u32; -const no_watch_item: WatchItemIndex = std.math.maxInt(WatchItemIndex); - -// Consumer-facing -watch_events: [128]WatchEvent, -changed_filepaths: [128]?[:0]u8, - -/// The platform-specific implementation of the watcher -platform: Platform, - -watchlist: WatchList, -watched_count: usize, -mutex: Mutex, - -fs: *bun.fs.FileSystem, -allocator: std.mem.Allocator, -watchloop_handle: ?std.Thread.Id = null, -cwd: string, -thread: std.Thread = undefined, -running: bool = true, -close_descriptors: bool = false, - -evict_list: [max_eviction_count]WatchItemIndex = undefined, -evict_list_i: WatchItemIndex = 0, - -ctx: *anyopaque, -onFileUpdate: *const fn (this: *anyopaque, events: []WatchEvent, changed_files: []?[:0]u8, watchlist: WatchList) void, -onError: *const fn (this: *anyopaque, err: bun.sys.Error) void, - -thread_lock: bun.DebugThreadLock = bun.DebugThreadLock.unlocked, - -/// Initializes a watcher. Each watcher is tied to some context type, which -/// recieves watch callbacks on the watcher thread. This function does not -/// actually start the watcher thread. -/// -/// const watcher = try Watcher.init(T, instance_of_t, fs, bun.default_allocator) -/// errdefer watcher.deinit(false); -/// try watcher.start(); -/// -/// To integrate a started watcher into module resolution: -/// -/// transpiler.resolver.watcher = watcher.getResolveWatcher(); -/// -/// To integrate a started watcher into bundle_v2: -/// -/// bundle_v2.bun_watcher = watcher; -pub fn init(comptime T: type, ctx: *T, fs: *bun.fs.FileSystem, allocator: std.mem.Allocator) !*Watcher { - const wrapped = struct { - fn onFileUpdateWrapped(ctx_opaque: *anyopaque, events: []WatchEvent, changed_files: []?[:0]u8, watchlist: WatchList) void { - T.onFileUpdate(@alignCast(@ptrCast(ctx_opaque)), events, changed_files, watchlist); - } - fn onErrorWrapped(ctx_opaque: *anyopaque, err: bun.sys.Error) void { - if (@hasDecl(T, "onWatchError")) { - T.onWatchError(@alignCast(@ptrCast(ctx_opaque)), err); - } else { - T.onError(@alignCast(@ptrCast(ctx_opaque)), err); - } - } - }; - - const watcher = try allocator.create(Watcher); - errdefer allocator.destroy(watcher); - watcher.* = Watcher{ - .fs = fs, - .allocator = allocator, - .watched_count = 0, - .watchlist = WatchList{}, - .mutex = .{}, - .cwd = fs.top_level_dir, - .ctx = ctx, - .onFileUpdate = &wrapped.onFileUpdateWrapped, - .onError = &wrapped.onErrorWrapped, - .platform = .{}, - .watch_events = undefined, - .changed_filepaths = [_]?[:0]u8{null} ** 128, - }; - - try Platform.init(&watcher.platform, fs.top_level_dir); - - return watcher; -} - -pub fn start(this: *Watcher) !void { - bun.assert(this.watchloop_handle == null); - this.thread = try std.Thread.spawn(.{}, threadMain, .{this}); -} - -pub fn deinit(this: *Watcher, close_descriptors: bool) void { - if (this.watchloop_handle != null) { - this.mutex.lock(); - defer this.mutex.unlock(); - this.close_descriptors = close_descriptors; - this.running = false; - } else { - if (close_descriptors and this.running) { - const fds = this.watchlist.items(.fd); - for (fds) |fd| { - _ = bun.sys.close(fd); - } - } - this.watchlist.deinit(this.allocator); - const allocator = this.allocator; - allocator.destroy(this); - } -} - -pub fn getHash(filepath: string) HashType { - return @as(HashType, @truncate(bun.hash(filepath))); -} - -pub const WatchItemIndex = u16; -pub const max_eviction_count = 8096; - -const log = bun.Output.scoped(.watcher, false); - -const WindowsWatcher = @import("./watcher/WindowsWatcher.zig"); -// TODO: some platform-specific behavior is implemented in -// this file instead of the platform-specific file. -// ideally, the constants above can be inlined -const Platform = switch (Environment.os) { - .linux => @import("./watcher/INotifyWatcher.zig"), - .mac => @import("./watcher/KEventWatcher.zig"), - .windows => WindowsWatcher, - else => @compileError("Unsupported platform"), -}; - -pub const WatchEvent = struct { - index: WatchItemIndex, - op: Op, - name_off: u8 = 0, - name_len: u8 = 0, - - pub fn names(this: WatchEvent, buf: []?[:0]u8) []?[:0]u8 { - if (this.name_len == 0) return &[_]?[:0]u8{}; - return buf[this.name_off..][0..this.name_len]; - } - - pub const Sorter = void; - - pub fn sortByIndex(_: Sorter, event: WatchEvent, rhs: WatchEvent) bool { - return event.index < rhs.index; - } - - pub fn merge(this: *WatchEvent, other: WatchEvent) void { - this.name_len += other.name_len; - this.op = Op{ - .delete = this.op.delete or other.op.delete, - .metadata = this.op.metadata or other.op.metadata, - .rename = this.op.rename or other.op.rename, - .write = this.op.write or other.op.write, - }; - } - - pub const Op = packed struct { - delete: bool = false, - metadata: bool = false, - rename: bool = false, - write: bool = false, - move_to: bool = false, - - pub fn merge(before: Op, after: Op) Op { - return .{ - .delete = before.delete or after.delete, - .write = before.write or after.write, - .metadata = before.metadata or after.metadata, - .rename = before.rename or after.rename, - .move_to = before.move_to or after.move_to, - }; - } - - pub fn format(op: Op, comptime _: []const u8, _: std.fmt.FormatOptions, w: anytype) !void { - try w.writeAll("{"); - var first = true; - inline for (comptime std.meta.fieldNames(Op)) |name| { - if (@field(op, name)) { - if (!first) { - try w.writeAll(","); - } - first = false; - try w.writeAll(name); - } - } - try w.writeAll("}"); - } - }; -}; - -pub const WatchItem = struct { - file_path: string, - // filepath hash for quick comparison - hash: u32, - loader: options.Loader, - fd: bun.FileDescriptor, - count: u32, - parent_hash: u32, - kind: Kind, - package_json: ?*PackageJSON, - eventlist_index: if (Environment.isLinux) Platform.EventListIndex else u0 = 0, - - pub const Kind = enum { file, directory }; -}; - -fn threadMain(this: *Watcher) !void { - this.watchloop_handle = std.Thread.getCurrentId(); - this.thread_lock.lock(); - Output.Source.configureNamedThread("File Watcher"); - - defer Output.flush(); - if (FeatureFlags.verbose_watcher) Output.prettyln("Watcher started", .{}); - - switch (this.watchLoop()) { - .err => |err| { - this.watchloop_handle = null; - this.platform.stop(); - if (this.running) { - this.onError(this.ctx, err); - } - }, - .result => {}, - } - - // deinit and close descriptors if needed - if (this.close_descriptors) { - const fds = this.watchlist.items(.fd); - for (fds) |fd| { - _ = bun.sys.close(fd); - } - } - this.watchlist.deinit(this.allocator); - - const allocator = this.allocator; - allocator.destroy(this); -} - -pub fn flushEvictions(this: *Watcher) void { - if (this.evict_list_i == 0) return; - defer this.evict_list_i = 0; - - // swapRemove messes up the order - // But, it only messes up the order if any elements in the list appear after the item being removed - // So if we just sort the list by the biggest index first, that should be fine - std.sort.pdq( - WatchItemIndex, - this.evict_list[0..this.evict_list_i], - {}, - comptime std.sort.desc(WatchItemIndex), - ); - - var slice = this.watchlist.slice(); - const fds = slice.items(.fd); - var last_item = no_watch_item; - - for (this.evict_list[0..this.evict_list_i]) |item| { - // catch duplicates, since the list is sorted, duplicates will appear right after each other - if (item == last_item) continue; - - if (!Environment.isWindows) { - // on mac and linux we can just close the file descriptor - // TODO do we need to call inotify_rm_watch on linux? - _ = bun.sys.close(fds[item]); - } - last_item = item; - } - - last_item = no_watch_item; - // This is split into two passes because reading the slice while modified is potentially unsafe. - for (this.evict_list[0..this.evict_list_i]) |item| { - if (item == last_item) continue; - this.watchlist.swapRemove(item); - last_item = item; - } -} - -fn watchLoop(this: *Watcher) bun.JSC.Maybe(void) { - while (this.running) { - // individual platform implementation will call onFileUpdate - switch (Platform.watchLoopCycle(this)) { - .err => |err| return .{ .err = err }, - .result => |iter| iter, - } - } - return .{ .result = {} }; -} - -fn appendFileAssumeCapacity( - this: *Watcher, - fd: bun.FileDescriptor, - file_path: string, - hash: HashType, - loader: options.Loader, - parent_hash: HashType, - package_json: ?*PackageJSON, - comptime copy_file_path: bool, -) bun.JSC.Maybe(void) { - if (comptime Environment.isWindows) { - // on windows we can only watch items that are in the directory tree of the top level dir - const rel = bun.path.isParentOrEqual(this.fs.top_level_dir, file_path); - if (rel == .unrelated) { - Output.warn("File {s} is not in the project directory and will not be watched\n", .{file_path}); - return .{ .result = {} }; - } - } - - const watchlist_id = this.watchlist.len; - - const file_path_: string = if (comptime copy_file_path) - bun.asByteSlice(this.allocator.dupeZ(u8, file_path) catch bun.outOfMemory()) - else - file_path; - - var item = WatchItem{ - .file_path = file_path_, - .fd = fd, - .hash = hash, - .count = 0, - .loader = loader, - .parent_hash = parent_hash, - .package_json = package_json, - .kind = .file, - }; - - if (comptime Environment.isMac) { - const KEvent = std.c.Kevent; - - // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html - var event = std.mem.zeroes(KEvent); - - event.flags = std.c.EV.ADD | std.c.EV.CLEAR | std.c.EV.ENABLE; - // we want to know about the vnode - event.filter = std.c.EVFILT.VNODE; - - event.fflags = std.c.NOTE.WRITE | std.c.NOTE.RENAME | std.c.NOTE.DELETE; - - // id - event.ident = @intCast(fd.int()); - - // Store the hash for fast filtering later - event.udata = @as(usize, @intCast(watchlist_id)); - var events: [1]KEvent = .{event}; - - // This took a lot of work to figure out the right permutation - // Basically: - // - We register the event here. - // our while(true) loop above receives notification of changes to any of the events created here. - _ = std.posix.system.kevent( - this.platform.fd.cast(), - @as([]KEvent, events[0..1]).ptr, - 1, - @as([]KEvent, events[0..1]).ptr, - 0, - null, - ); - } else if (comptime Environment.isLinux) { - // var file_path_to_use_ = std.mem.trimRight(u8, file_path_, "/"); - // var buf: [bun.MAX_PATH_BYTES+1]u8 = undefined; - // bun.copy(u8, &buf, file_path_to_use_); - // buf[file_path_to_use_.len] = 0; - var buf = file_path_.ptr; - const slice: [:0]const u8 = buf[0..file_path_.len :0]; - item.eventlist_index = switch (this.platform.watchPath(slice)) { - .err => |err| return .{ .err = err }, - .result => |r| r, - }; - } - - this.watchlist.appendAssumeCapacity(item); - return .{ .result = {} }; -} - -fn appendDirectoryAssumeCapacity( - this: *Watcher, - stored_fd: bun.FileDescriptor, - file_path: string, - hash: HashType, - comptime copy_file_path: bool, -) bun.JSC.Maybe(WatchItemIndex) { - if (comptime Environment.isWindows) { - // on windows we can only watch items that are in the directory tree of the top level dir - const rel = bun.path.isParentOrEqual(this.fs.top_level_dir, file_path); - if (rel == .unrelated) { - Output.warn("Directory {s} is not in the project directory and will not be watched\n", .{file_path}); - return .{ .result = no_watch_item }; - } - } - - const fd = brk: { - if (stored_fd != .zero) break :brk stored_fd; - break :brk switch (bun.sys.openA(file_path, 0, 0)) { - .err => |err| return .{ .err = err }, - .result => |fd| fd, - }; - }; - - const parent_hash = getHash(bun.fs.PathName.init(file_path).dirWithTrailingSlash()); - - const file_path_: string = if (comptime copy_file_path) - bun.asByteSlice(this.allocator.dupeZ(u8, file_path) catch bun.outOfMemory()) - else - file_path; - - const watchlist_id = this.watchlist.len; - - var item = WatchItem{ - .file_path = file_path_, - .fd = fd, - .hash = hash, - .count = 0, - .loader = options.Loader.file, - .parent_hash = parent_hash, - .kind = .directory, - .package_json = null, - }; - - if (Environment.isMac) { - const KEvent = std.c.Kevent; - - // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html - var event = std.mem.zeroes(KEvent); - - event.flags = std.c.EV.ADD | std.c.EV.CLEAR | std.c.EV.ENABLE; - // we want to know about the vnode - event.filter = std.c.EVFILT.VNODE; - - // monitor: - // - Write - // - Rename - // - Delete - event.fflags = std.c.NOTE.WRITE | std.c.NOTE.RENAME | std.c.NOTE.DELETE; - - // id - event.ident = @intCast(fd.int()); - - // Store the hash for fast filtering later - event.udata = @as(usize, @intCast(watchlist_id)); - var events: [1]KEvent = .{event}; - - // This took a lot of work to figure out the right permutation - // Basically: - // - We register the event here. - // our while(true) loop above receives notification of changes to any of the events created here. - _ = std.posix.system.kevent( - this.platform.fd.cast(), - @as([]KEvent, events[0..1]).ptr, - 1, - @as([]KEvent, events[0..1]).ptr, - 0, - null, - ); - } else if (Environment.isLinux) { - const file_path_to_use_ = std.mem.trimRight(u8, file_path_, "/"); - var buf: bun.PathBuffer = undefined; - bun.copy(u8, &buf, file_path_to_use_); - buf[file_path_to_use_.len] = 0; - const slice: [:0]u8 = buf[0..file_path_to_use_.len :0]; - item.eventlist_index = switch (this.platform.watchDir(slice)) { - .err => |err| return .{ .err = err }, - .result => |r| r, - }; - } - - this.watchlist.appendAssumeCapacity(item); - return .{ - .result = @as(WatchItemIndex, @truncate(this.watchlist.len - 1)), - }; -} - -// Below is platform-independent - -pub fn appendFileMaybeLock( - this: *Watcher, - fd: bun.FileDescriptor, - file_path: string, - hash: HashType, - loader: options.Loader, - dir_fd: bun.FileDescriptor, - package_json: ?*PackageJSON, - comptime copy_file_path: bool, - comptime lock: bool, -) bun.JSC.Maybe(void) { - if (comptime lock) this.mutex.lock(); - defer if (comptime lock) this.mutex.unlock(); - bun.assert(file_path.len > 1); - const pathname = bun.fs.PathName.init(file_path); - - const parent_dir = pathname.dirWithTrailingSlash(); - const parent_dir_hash: HashType = getHash(parent_dir); - - var parent_watch_item: ?WatchItemIndex = null; - const autowatch_parent_dir = (comptime FeatureFlags.watch_directories) and this.isEligibleDirectory(parent_dir); - if (autowatch_parent_dir) { - var watchlist_slice = this.watchlist.slice(); - - if (dir_fd != .zero) { - const fds = watchlist_slice.items(.fd); - if (std.mem.indexOfScalar(bun.FileDescriptor, fds, dir_fd)) |i| { - parent_watch_item = @as(WatchItemIndex, @truncate(i)); - } - } - - if (parent_watch_item == null) { - const hashes = watchlist_slice.items(.hash); - if (std.mem.indexOfScalar(HashType, hashes, parent_dir_hash)) |i| { - parent_watch_item = @as(WatchItemIndex, @truncate(i)); - } - } - } - this.watchlist.ensureUnusedCapacity(this.allocator, 1 + @as(usize, @intCast(@intFromBool(parent_watch_item == null)))) catch bun.outOfMemory(); - - if (autowatch_parent_dir) { - parent_watch_item = parent_watch_item orelse switch (this.appendDirectoryAssumeCapacity(dir_fd, parent_dir, parent_dir_hash, copy_file_path)) { - .err => |err| return .{ .err = err }, - .result => |r| r, - }; - } - - switch (this.appendFileAssumeCapacity( - fd, - file_path, - hash, - loader, - parent_dir_hash, - package_json, - copy_file_path, - )) { - .err => |err| return .{ .err = err }, - .result => {}, - } - - if (comptime FeatureFlags.verbose_watcher) { - if (strings.indexOf(file_path, this.cwd)) |i| { - Output.prettyln("Added ./{s} to watch list.", .{file_path[i + this.cwd.len ..]}); - } else { - Output.prettyln("Added {s} to watch list.", .{file_path}); - } - } - - return .{ .result = {} }; -} - -inline fn isEligibleDirectory(this: *Watcher, dir: string) bool { - return strings.contains(dir, this.fs.top_level_dir) and !strings.contains(dir, "node_modules"); -} - -pub fn appendFile( - this: *Watcher, - fd: bun.FileDescriptor, - file_path: string, - hash: HashType, - loader: options.Loader, - dir_fd: bun.FileDescriptor, - package_json: ?*PackageJSON, - comptime copy_file_path: bool, -) bun.JSC.Maybe(void) { - return appendFileMaybeLock(this, fd, file_path, hash, loader, dir_fd, package_json, copy_file_path, true); -} - -pub fn addDirectory( - this: *Watcher, - fd: bun.FileDescriptor, - file_path: string, - hash: HashType, - comptime copy_file_path: bool, -) bun.JSC.Maybe(WatchItemIndex) { - this.mutex.lock(); - defer this.mutex.unlock(); - - if (this.indexOf(hash)) |idx| { - return .{ .result = @truncate(idx) }; - } - - this.watchlist.ensureUnusedCapacity(this.allocator, 1) catch bun.outOfMemory(); - - return this.appendDirectoryAssumeCapacity(fd, file_path, hash, copy_file_path); -} - -pub fn addFile( - this: *Watcher, - fd: bun.FileDescriptor, - file_path: string, - hash: HashType, - loader: options.Loader, - dir_fd: bun.FileDescriptor, - package_json: ?*PackageJSON, - comptime copy_file_path: bool, -) bun.JSC.Maybe(void) { - // This must lock due to concurrent transpiler - this.mutex.lock(); - defer this.mutex.unlock(); - - if (this.indexOf(hash)) |index| { - if (comptime FeatureFlags.atomic_file_watcher) { - // On Linux, the file descriptor might be out of date. - if (fd.int() > 0) { - var fds = this.watchlist.items(.fd); - fds[index] = fd; - } - } - return .{ .result = {} }; - } - - return this.appendFileMaybeLock(fd, file_path, hash, loader, dir_fd, package_json, copy_file_path, false); -} - -pub fn indexOf(this: *Watcher, hash: HashType) ?u32 { - for (this.watchlist.items(.hash), 0..) |other, i| { - if (hash == other) { - return @as(u32, @truncate(i)); - } - } - return null; -} - -pub fn remove(this: *Watcher, hash: HashType) void { - this.mutex.lock(); - defer this.mutex.unlock(); - if (this.indexOf(hash)) |index| { - this.removeAtIndex(@truncate(index), hash, &[_]HashType{}, .file); - } -} - -pub fn removeAtIndex(this: *Watcher, index: WatchItemIndex, hash: HashType, parents: []HashType, comptime kind: WatchItem.Kind) void { - bun.assert(index != no_watch_item); - - this.evict_list[this.evict_list_i] = index; - this.evict_list_i += 1; - - if (comptime kind == .directory) { - for (parents) |parent| { - if (parent == hash) { - this.evict_list[this.evict_list_i] = @as(WatchItemIndex, @truncate(parent)); - this.evict_list_i += 1; - } - } - } -} - -pub fn getResolveWatcher(watcher: *Watcher) bun.resolver.AnyResolveWatcher { - return bun.resolver.ResolveWatcher(*@This(), onMaybeWatchDirectory).init(watcher); -} - -pub fn onMaybeWatchDirectory(watch: *Watcher, file_path: string, dir_fd: bun.StoredFileDescriptorType) void { - // We don't want to watch: - // - Directories outside the root directory - // - Directories inside node_modules - if (std.mem.indexOf(u8, file_path, "node_modules") == null and std.mem.indexOf(u8, file_path, watch.fs.top_level_dir) != null) { - _ = watch.addDirectory(dir_fd, file_path, getHash(file_path), false); - } -} - -const std = @import("std"); -const bun = @import("root").bun; -const string = bun.string; -const Output = bun.Output; -const Global = bun.Global; -const Environment = bun.Environment; -const strings = bun.strings; -const stringZ = bun.stringZ; -const FeatureFlags = bun.FeatureFlags; -const options = @import("./options.zig"); -const Mutex = bun.Mutex; -const Futex = @import("./futex.zig"); -const PackageJSON = @import("./resolver/package_json.zig").PackageJSON; diff --git a/src/watcher/INotifyWatcher.zig b/src/watcher/INotifyWatcher.zig index d86694d6338bf3..653841431bf7dd 100644 --- a/src/watcher/INotifyWatcher.zig +++ b/src/watcher/INotifyWatcher.zig @@ -1,7 +1,7 @@ //! Bun's filesystem watcher implementation for linux using inotify //! https://man7.org/linux/man-pages/man7/inotify.7.html const INotifyWatcher = @This(); -const log = Output.scoped(.inotify, false); +const log = Output.scoped(.watcher, false); // inotify events are variable-sized, so a byte buffer is used (also needed // since communication is done via the `read` syscall). what is notable about diff --git a/src/watcher/KEventWatcher.zig b/src/watcher/KEventWatcher.zig index e1037db01a3506..b6d6181450e5ca 100644 --- a/src/watcher/KEventWatcher.zig +++ b/src/watcher/KEventWatcher.zig @@ -1,4 +1,5 @@ const KEventWatcher = @This(); +const log = Output.scoped(.watcher, false); pub const EventListIndex = u32; const KEvent = std.c.Kevent; @@ -48,32 +49,29 @@ pub fn watchLoopCycle(this: *Watcher) bun.JSC.Maybe(void) { var count = std.posix.system.kevent( this.platform.fd.cast(), - @as([*]KEvent, changelist), + changelist, 0, - @as([*]KEvent, changelist), + changelist, 128, - - null, + null, // timeout ); // Give the events more time to coalesce if (count < 128 / 2) { const remain = 128 - count; - var timespec = std.posix.timespec{ .sec = 0, .nsec = 100_000 }; const extra = std.posix.system.kevent( this.platform.fd.cast(), - @as([*]KEvent, changelist[@as(usize, @intCast(count))..].ptr), + changelist[@intCast(count)..].ptr, 0, - @as([*]KEvent, changelist[@as(usize, @intCast(count))..].ptr), + changelist[@intCast(count)..].ptr, remain, - - ×pec, + &.{ .sec = 0, .nsec = 100_000 }, // 0.0001 seconds ); count += extra; } - var changes = changelist[0..@as(usize, @intCast(@max(0, count)))]; + var changes = changelist[0..@intCast(@max(0, count))]; var watchevents = this.watch_events[0..changes.len]; var out_len: usize = 0; if (changes.len > 0) { diff --git a/test/cli/test/__snapshots__/coverage.test.ts.snap b/test/cli/test/__snapshots__/coverage.test.ts.snap index 92503498655382..12122efc4d3238 100644 --- a/test/cli/test/__snapshots__/coverage.test.ts.snap +++ b/test/cli/test/__snapshots__/coverage.test.ts.snap @@ -16,8 +16,8 @@ SF:demo2.ts FNF:2 FNH:1 DA:2,28 -DA:4,10 -DA:6,10 +DA:4,11 +DA:6,9 DA:9,0 DA:10,0 DA:11,1 diff --git a/test/js/bun/http/bun-serve-html-entry.test.ts b/test/js/bun/http/bun-serve-html-entry.test.ts index 250629671db154..b31c9e88fbd988 100644 --- a/test/js/bun/http/bun-serve-html-entry.test.ts +++ b/test/js/bun/http/bun-serve-html-entry.test.ts @@ -95,7 +95,7 @@ test("bun ./index.html", async () => { cmd: [bunExe(), "index.html", "--port=0"], env: { ...bunEnv, - NODE_ENV: undefined, + NODE_ENV: "production", }, cwd: dir, stdout: "pipe", @@ -127,7 +127,7 @@ test("bun ./index.html", async () => { expect(cssResponse.headers.get("content-type")).toContain("text/css"); const css = await cssResponse.text(); expect(css).toContain(".container"); - expect(css).toContain("max-width: 800px"); + expect(css).toContain("max-width:800px"); } // Get and verify the bundled JS @@ -213,7 +213,7 @@ test("bun ./index.html ./about.html", async () => { cmd: [bunExe(), "index.html", "about.html", "--port=0"], env: { ...bunEnv, - NODE_ENV: undefined, + NODE_ENV: "production", }, cwd: dir, stdout: "pipe", @@ -258,7 +258,7 @@ test("bun ./index.html ./about.html", async () => { expect(cssResponse.status).toBe(200); const css = await cssResponse.text(); expect(css).toContain(".container"); - expect(css).toContain("max-width: 800px"); + expect(css).toContain("max-width:800px"); } // Verify both JS bundles work @@ -408,7 +408,7 @@ test("bun *.html", async () => { cmd: [bunExe(), "*.html", "--port=0"], env: { ...bunEnv, - NODE_ENV: undefined, + NODE_ENV: "production", }, cwd: dir, stdout: "pipe", @@ -450,9 +450,9 @@ test("bun *.html", async () => { const cssResponse = await fetch(new URL(cssMatches[0]!, serverUrl).href); expect(cssResponse.status).toBe(200); const css = await cssResponse.text(); - expect(css).toContain("nav {"); - expect(css).toContain(".container {"); - expect(css).toContain("form {"); + expect(css).toContain("nav{"); + expect(css).toContain(".container{"); + expect(css).toContain("form{"); // Verify each page has its own JS functionality const jsMatches = responses.map(html => html.match(/src="(\/chunk-[a-z0-9]+\.js)"/)?.[1]!); diff --git a/test/js/bun/http/bun-serve-html.test.ts b/test/js/bun/http/bun-serve-html.test.ts index 4c0860fec4cb0d..f7206fc2801382 100644 --- a/test/js/bun/http/bun-serve-html.test.ts +++ b/test/js/bun/http/bun-serve-html.test.ts @@ -199,21 +199,21 @@ console.log("How...dashing?"); const sourceMap = await (await fetch(new URL(sourceMapURL, "http://" + hostname + ":" + port))).json(); sourceMap.sourcesContent = sourceMap.sourcesContent.map(a => a.trim()); expect(JSON.stringify(sourceMap, null, 2)).toMatchInlineSnapshot(` -"{ - "version": 3, - "sources": [ - "script.js", - "dashboard.js" - ], - "sourcesContent": [ - "let count = 0;\\n const button = document.getElementById('counter');\\n button.addEventListener('click', () => {\\n count++;\\n button.textContent = \`Click me: \${count}\`;\\n });", - "import './script.js';\\n // Additional dashboard-specific code could go here\\n console.log(\\"How...dashing?\\")" - ], - "mappings": ";AACM,IAAI,QAAQ;AACZ,IAAM,SAAS,SAAS,eAAe,SAAS;AAChD,OAAO,iBAAiB,SAAS,MAAM;AACrC;AACA,SAAO,cAAc,aAAa;AAAA,CACnC;;;ACHD,QAAQ,IAAI,gBAAgB;", - "debugId": "0B3DD451DC3D66B564756E2164756E21", - "names": [] -}" -`); + "{ + "version": 3, + "sources": [ + "script.js", + "dashboard.js" + ], + "sourcesContent": [ + "let count = 0;\\n const button = document.getElementById('counter');\\n button.addEventListener('click', () => {\\n count++;\\n button.textContent = \`Click me: \${count}\`;\\n });", + "import './script.js';\\n // Additional dashboard-specific code could go here\\n console.log(\\"How...dashing?\\")" + ], + "mappings": ";AACM,IAAI,QAAQ;AACZ,IAAM,SAAS,SAAS,eAAe,SAAS;AAChD,OAAO,iBAAiB,SAAS,MAAM;AAAA,EACrC;AAAA,EACA,OAAO,cAAc,aAAa;AAAA,CACnC;;;ACHD,QAAQ,IAAI,gBAAgB;", + "debugId": "0B3DD451DC3D66B564756E2164756E21", + "names": [] + }" + `); const headers = response.headers.toJSON(); headers.date = ""; headers.sourcemap = headers.sourcemap.replace(/chunk-[a-z0-9]+\.js.map/g, "chunk-HASH.js.map"); @@ -593,18 +593,20 @@ async function waitForServer( port: number; hostname: string; }> { + console.log("waitForServer", dir, entryPoints); let defer = Promise.withResolvers<{ subprocess: Subprocess; port: number; hostname: string; }>(); const process = Bun.spawn({ - cmd: [bunExe(), join(import.meta.dir, "bun-serve-static-fixture.js")], + cmd: [bunExe(), "--no-hmr", join(import.meta.dir, "bun-serve-static-fixture.js")], env: { ...bunEnv, NODE_ENV: undefined, }, cwd: dir, + stdio: ["inherit", "inherit", "inherit"], ipc(message, subprocess) { subprocess.send({ files: entryPoints, diff --git a/test/js/bun/util/inspect-error.test.js b/test/js/bun/util/inspect-error.test.js index a439b5c5ad2dc8..0405064cba6dde 100644 --- a/test/js/bun/util/inspect-error.test.js +++ b/test/js/bun/util/inspect-error.test.js @@ -110,18 +110,18 @@ test("Error inside minified file (no color) ", () => { .trim(), ), ).toMatchInlineSnapshot(` -"21 | exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=Z; -22 | exports.cache=function(a){return function(){var b=U.current;if(!b)return a.apply(null,arguments);var c=b.getCacheForType(V);b=c.get(a);void 0===b&&(b=W(),c.set(a,b));c=0;for(var f=arguments.length;c ([dir]/inspect-error-fixture.min.js:26:2846) - at ([dir]/inspect-error-fixture.min.js:26:2890) - at ([dir]/inspect-error.test.js:102:5)" -`); + error: error inside long minified file! + at ([dir]/inspect-error-fixture.min.js:26:2846) + at ([dir]/inspect-error-fixture.min.js:26:2890) + at ([dir]/inspect-error.test.js:101:7)" + `); } }); @@ -141,18 +141,18 @@ test("Error inside minified file (color) ", () => { ).trim(), ), ).toMatchInlineSnapshot(` -"21 | exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=Z; -22 | exports.cache=function(a){return function(){var b=U.current;if(!b)return a.apply(null,arguments);var c=b.getCacheForType(V);b=c.get(a);void 0===b&&(b=W(),c.set(a,b));c=0;for(var f=arguments.length;c ([dir]/inspect-error-fixture.min.js:26:2846) - at ([dir]/inspect-error-fixture.min.js:26:2890) - at ([dir]/inspect-error.test.js:130:5)" -`); + error: error inside long minified file! + at ([dir]/inspect-error-fixture.min.js:26:2846) + at ([dir]/inspect-error-fixture.min.js:26:2890) + at ([dir]/inspect-error.test.js:129:7)" + `); } }); From 584db03a74f118873e9b73ab0c22c708c86a4aed Mon Sep 17 00:00:00 2001 From: Michael H Date: Sat, 8 Feb 2025 20:27:15 +1100 Subject: [PATCH 02/25] `bun pm pack` support `"files"` starting with `./` (#17135) Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> --- src/cli/pack_command.zig | 4 +++- test/cli/install/bun-pack.test.ts | 33 +++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/cli/pack_command.zig b/src/cli/pack_command.zig index dc9db0f63a364d..b23e5ebaaf5f64 100644 --- a/src/cli/pack_command.zig +++ b/src/cli/pack_command.zig @@ -1356,10 +1356,12 @@ pub const PackCommand = struct { var includes: std.ArrayListUnmanaged(Pattern) = .{}; defer includes.deinit(ctx.allocator); + var path_buf: PathBuffer = undefined; var files_array = _files_array; while (files_array.next()) |files_entry| { if (files_entry.asString(ctx.allocator)) |file_entry_str| { - const parsed = try Pattern.fromUTF8(ctx.allocator, file_entry_str) orelse continue; + const normalized = bun.path.normalizeBuf(file_entry_str, &path_buf, .posix); + const parsed = try Pattern.fromUTF8(ctx.allocator, normalized) orelse continue; try includes.append(ctx.allocator, parsed); continue; } diff --git a/test/cli/install/bun-pack.test.ts b/test/cli/install/bun-pack.test.ts index ac74efa8e7a6dc..482a5cc56776d9 100644 --- a/test/cli/install/bun-pack.test.ts +++ b/test/cli/install/bun-pack.test.ts @@ -921,7 +921,7 @@ describe("files", () => { write( join(packageDir, "package.json"), JSON.stringify({ - name: "pack-files-3", + name: "pack-files-2", version: "1.2.3", files: ["index.js"], }), @@ -932,16 +932,41 @@ describe("files", () => { ]); await pack(packageDir, bunEnv); - const tarball = readTarball(join(packageDir, "pack-files-3-1.2.3.tgz")); + const tarball = readTarball(join(packageDir, "pack-files-2-1.2.3.tgz")); expect(tarball.entries).toMatchObject([{ "pathname": "package/package.json" }, { "pathname": "package/index.js" }]); }); + test("matches './' as the root", async () => { + await Promise.all([ + write( + join(packageDir, "package.json"), + JSON.stringify({ + name: "pack-files-3", + version: "1.2.3", + files: ["./dist", "!./subdir", "!./dist/index.js", "./////src//index.ts"], + }), + ), + write(join(packageDir, "dist", "index.js"), "console.log('hello ./dist/index.js')"), + write(join(packageDir, "subdir", "index.js"), "console.log('hello ./subdir/index.js')"), + write(join(packageDir, "src", "dist", "index.js"), "console.log('hello ./src/dist/index.js')"), + write(join(packageDir, "src", "index.ts"), "console.log('hello ./src/index.ts')"), + ]); + + await pack(packageDir, bunEnv); + const tarball = readTarball(join(packageDir, "pack-files-3-1.2.3.tgz")); + expect(tarball.entries).toMatchObject([ + { "pathname": "package/package.json" }, + { "pathname": "package/dist/index.js" }, + { "pathname": "package/src/index.ts" }, + ]); + }); + test("recursive only if leading **/", async () => { await Promise.all([ write( join(packageDir, "package.json"), JSON.stringify({ - name: "pack-files-2", + name: "pack-files-4", version: "1.2.123", files: ["**/index.js"], }), @@ -953,7 +978,7 @@ describe("files", () => { ]); await pack(packageDir, bunEnv); - const tarball = readTarball(join(packageDir, "pack-files-2-1.2.123.tgz")); + const tarball = readTarball(join(packageDir, "pack-files-4-1.2.123.tgz")); expect(tarball.entries).toMatchObject([ { "pathname": "package/package.json" }, { "pathname": "package/index.js" }, From 2644bad5d472e4306232bc7cafa61d93b978e90a Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Sat, 8 Feb 2025 05:23:41 -0800 Subject: [PATCH 03/25] Fix lexing out of bounds by one (#17168) --- .buildkite/ci.mjs | 2 ++ src/js_lexer.zig | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/.buildkite/ci.mjs b/.buildkite/ci.mjs index 98506933dee565..7b07dafb47448d 100755 --- a/.buildkite/ci.mjs +++ b/.buildkite/ci.mjs @@ -391,6 +391,8 @@ function getBuildEnv(target, options) { ENABLE_ASSERTIONS: release ? "OFF" : "ON", ENABLE_LOGS: release ? "OFF" : "ON", ABI: abi === "musl" ? "musl" : undefined, + + CMAKE_TLS_VERIFY: "0", }; } diff --git a/src/js_lexer.zig b/src/js_lexer.zig index 7c9dd41e91f5f7..c9dd2d56ac5fde 100644 --- a/src/js_lexer.zig +++ b/src/js_lexer.zig @@ -798,11 +798,18 @@ fn NewLexer_( } inline fn nextCodepointSlice(it: *LexerType) []const u8 { + if (it.current >= it.source.contents.len) { + return ""; + } const cp_len = strings.wtf8ByteSequenceLengthWithInvalid(it.source.contents.ptr[it.current]); return if (!(cp_len + it.current > it.source.contents.len)) it.source.contents[it.current .. cp_len + it.current] else ""; } inline fn nextCodepoint(it: *LexerType) CodePoint { + if (it.current >= it.source.contents.len) { + it.end = it.source.contents.len; + return -1; + } const cp_len = strings.wtf8ByteSequenceLengthWithInvalid(it.source.contents.ptr[it.current]); const slice = if (!(cp_len + it.current > it.source.contents.len)) it.source.contents[it.current .. cp_len + it.current] else ""; From 14164920b52e9686a656aa96bc719b6b39e48b54 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 9 Feb 2025 00:36:36 -0800 Subject: [PATCH 04/25] deps: update sqlite to 3.490.0 (#17201) Co-authored-by: Jarred-Sumner --- src/bun.js/bindings/sqlite/sqlite3.c | 4070 +++++++++++--------- src/bun.js/bindings/sqlite/sqlite3_local.h | 153 +- 2 files changed, 2363 insertions(+), 1860 deletions(-) diff --git a/src/bun.js/bindings/sqlite/sqlite3.c b/src/bun.js/bindings/sqlite/sqlite3.c index e593122116b043..5cb30c81a53595 100644 --- a/src/bun.js/bindings/sqlite/sqlite3.c +++ b/src/bun.js/bindings/sqlite/sqlite3.c @@ -1,7 +1,7 @@ // clang-format off /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.48.0. By combining all the individual C code files into this +** version 3.49.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -19,7 +19,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** d2fe6b05f38d9d7cd78c5d252e99ac59f1ae with changes in files: +** 4a7dd425dc2a0e5082a9049c9b4a9d4f199a with changes in files: ** ** */ @@ -466,9 +466,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.48.0" -#define SQLITE_VERSION_NUMBER 3048000 -#define SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010" +#define SQLITE_VERSION "3.49.0" +#define SQLITE_VERSION_NUMBER 3049000 +#define SQLITE_SOURCE_ID "2025-02-06 11:55:18 4a7dd425dc2a0e5082a9049c9b4a9d4f199a71583d014c24b4cfe276c5a77cde" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -2531,7 +2531,15 @@ struct sqlite3_mem_methods { ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that -** can be passed as the second argument to the [sqlite3_db_config()] interface. +** can be passed as the second parameter to the [sqlite3_db_config()] interface. +** +** The [sqlite3_db_config()] interface is a var-args functions. It takes a +** variable number of parameters, though always at least two. The number of +** parameters passed into sqlite3_db_config() depends on which of these +** constants is given as the second parameter. This documentation page +** refers to parameters beyond the second as "arguments". Thus, when this +** page says "the N-th argument" it means "the N-th parameter past the +** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()". ** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications @@ -2543,8 +2551,14 @@ struct sqlite3_mem_methods { **
** [[SQLITE_DBCONFIG_LOOKASIDE]] **
SQLITE_DBCONFIG_LOOKASIDE
-**
^This option takes three additional arguments that determine the -** [lookaside memory allocator] configuration for the [database connection]. +**
The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the +** configuration of the lookaside memory allocator within a database +** connection. +** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are not +** in the [DBCONFIG arguments|usual format]. +** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two, +** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE +** should have a total of five parameters. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a ** pointer to a memory buffer to use for lookaside memory. ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb @@ -2567,7 +2581,8 @@ struct sqlite3_mem_methods { ** [[SQLITE_DBCONFIG_ENABLE_FKEY]] **
SQLITE_DBCONFIG_ENABLE_FKEY
**
^This option is used to enable or disable the enforcement of -** [foreign key constraints]. There should be two additional arguments. +** [foreign key constraints]. This is the same setting that is +** enabled or disabled by the [PRAGMA foreign_keys] statement. ** The first argument is an integer which is 0 to disable FK enforcement, ** positive to enable FK enforcement or negative to leave FK enforcement ** unchanged. The second parameter is a pointer to an integer into which @@ -2589,13 +2604,13 @@ struct sqlite3_mem_methods { **

Originally this option disabled all triggers. ^(However, since ** SQLite version 3.35.0, TEMP triggers are still allowed even if ** this option is off. So, in other words, this option now only disables -** triggers in the main database schema or in the schemas of ATTACH-ed +** triggers in the main database schema or in the schemas of [ATTACH]-ed ** databases.)^

** ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] **
SQLITE_DBCONFIG_ENABLE_VIEW
**
^This option is used to enable or disable [CREATE VIEW | views]. -** There should be two additional arguments. +** There must be two additional arguments. ** The first argument is an integer which is 0 to disable views, ** positive to enable views or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which @@ -2614,7 +2629,7 @@ struct sqlite3_mem_methods { **
^This option is used to enable or disable the ** [fts3_tokenizer()] function which is part of the ** [FTS3] full-text search engine extension. -** There should be two additional arguments. +** There must be two additional arguments. ** The first argument is an integer which is 0 to disable fts3_tokenizer() or ** positive to enable fts3_tokenizer() or negative to leave the setting ** unchanged. @@ -2629,7 +2644,7 @@ struct sqlite3_mem_methods { ** interface independently of the [load_extension()] SQL function. ** The [sqlite3_enable_load_extension()] API enables or disables both the ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. -** There should be two additional arguments. +** There must be two additional arguments. ** When the first argument to this interface is 1, then only the C-API is ** enabled and the SQL function remains disabled. If the first argument to ** this interface is 0, then both the C-API and the SQL function are disabled. @@ -2643,23 +2658,30 @@ struct sqlite3_mem_methods { ** ** [[SQLITE_DBCONFIG_MAINDBNAME]]
SQLITE_DBCONFIG_MAINDBNAME
**
^This option is used to change the name of the "main" database -** schema. ^The sole argument is a pointer to a constant UTF8 string -** which will become the new schema name in place of "main". ^SQLite -** does not make a copy of the new main schema name string, so the application -** must ensure that the argument passed into this DBCONFIG option is unchanged -** until after the database connection closes. +** schema. This option does not follow the +** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format]. +** This option takes exactly one additional argument so that the +** [sqlite3_db_config()] call has a total of three parameters. The +** extra argument must be a pointer to a constant UTF8 string which +** will become the new schema name in place of "main". ^SQLite does +** not make a copy of the new main schema name string, so the application +** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME +** is unchanged until after the database connection closes. **
** ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] **
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
-**
Usually, when a database in wal mode is closed or detached from a -** database handle, SQLite checks if this will mean that there are now no -** connections at all to the database. If so, it performs a checkpoint -** operation before closing the connection. This option may be used to -** override this behavior. The first parameter passed to this operation -** is an integer - positive to disable checkpoints-on-close, or zero (the -** default) to enable them, and negative to leave the setting unchanged. -** The second parameter is a pointer to an integer +**
Usually, when a database in [WAL mode] is closed or detached from a +** database handle, SQLite checks if if there are other connections to the +** same database, and if there are no other database connection (if the +** connection being closed is the last open connection to the database), +** then SQLite performs a [checkpoint] before closing the connection and +** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can +** be used to override that behavior. The first argument passed to this +** operation (the third parameter to [sqlite3_db_config()]) is an integer +** which is positive to disable checkpoints-on-close, or zero (the default) +** to enable them, and negative to leave the setting unchanged. +** The second argument (the fourth parameter) is a pointer to an integer ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. **
@@ -2820,7 +2842,7 @@ struct sqlite3_mem_methods { ** statistics. For statistics to be collected, the flag must be set on ** the database handle both when the SQL statement is prepared and when it ** is stepped. The flag is set (collection of statistics is enabled) -** by default. This option takes two arguments: an integer and a pointer to +** by default.

This option takes two arguments: an integer and a pointer to ** an integer.. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the statement scanstatus option. If the second argument ** is not NULL, then the value of the statement scanstatus setting after @@ -2834,7 +2856,7 @@ struct sqlite3_mem_methods { ** in which tables and indexes are scanned so that the scans start at the end ** and work toward the beginning rather than starting at the beginning and ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the -** same as setting [PRAGMA reverse_unordered_selects]. This option takes +** same as setting [PRAGMA reverse_unordered_selects].

This option takes ** two arguments which are an integer and a pointer to an integer. The first ** argument is 1, 0, or -1 to enable, disable, or leave unchanged the ** reverse scan order flag, respectively. If the second argument is not NULL, @@ -2843,7 +2865,76 @@ struct sqlite3_mem_methods { ** first argument. ** ** +** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]] +**

SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE
+**
The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables +** the ability of the [ATTACH DATABASE] SQL command to create a new database +** file if the database filed named in the ATTACH command does not already +** exist. This ability of ATTACH to create a new database is enabled by +** default. Applications can disable or reenable the ability for ATTACH to +** create new database files using this DBCONFIG option.

+** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the attach-create flag, respectively. If the second +** argument is not NULL, then 0 or 1 is written into the integer that the +** second argument points to depending on if the attach-create flag is set +** after processing the first argument. +**

+** +** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]] +**
SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE
+**
The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the +** ability of the [ATTACH DATABASE] SQL command to open a database for writing. +** This capability is enabled by default. Applications can disable or +** reenable this capability using the current DBCONFIG option. If the +** the this capability is disabled, the [ATTACH] command will still work, +** but the database will be opened read-only. If this option is disabled, +** then the ability to create a new database using [ATTACH] is also disabled, +** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE] +** option.

+** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the ability to ATTACH another database for writing, +** respectively. If the second argument is not NULL, then 0 or 1 is written +** into the integer to which the second argument points, depending on whether +** the ability to ATTACH a read/write database is enabled or disabled +** after processing the first argument. +**

+** +** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]] +**
SQLITE_DBCONFIG_ENABLE_COMMENTS
+**
The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the +** ability to include comments in SQL text. Comments are enabled by default. +** An application can disable or reenable comments in SQL text using this +** DBCONFIG option.

+** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the ability to use comments in SQL text, +** respectively. If the second argument is not NULL, then 0 or 1 is written +** into the integer that the second argument points to depending on if +** comments are allowed in SQL text after processing the first argument. +**

+** **
+** +** [[DBCONFIG arguments]]

Arguments To SQLITE_DBCONFIG Options

+** +**

Most of the SQLITE_DBCONFIG options take two arguments, so that the +** overall call to [sqlite3_db_config()] has a total of four parameters. +** The first argument (the third parameter to sqlite3_db_config()) is a integer. +** The second argument is a pointer to an integer. If the first argument is 1, +** then the option becomes enabled. If the first integer argument is 0, then the +** option is disabled. If the first argument is -1, then the option setting +** is unchanged. The second argument, the pointer to an integer, may be NULL. +** If the second argument is not NULL, then a value of 0 or 1 is written into +** the integer to which the second argument points, depending on whether the +** setting is disabled or enabled after applying any changes specified by +** the first argument. +** +**

While most SQLITE_DBCONFIG options use the argument format +** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] +** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the +** documentation of those exceptional options for details. */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ @@ -2865,7 +2956,10 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -11068,8 +11162,9 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c /* ** CAPI3REF: Serialize a database ** -** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory -** that is a serialization of the S database on [database connection] D. +** The sqlite3_serialize(D,S,P,F) interface returns a pointer to +** memory that is a serialization of the S database on +** [database connection] D. If S is a NULL pointer, the main database is used. ** If P is not a NULL pointer, then the size of the database in bytes ** is written into *P. ** @@ -14878,7 +14973,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #define TK_ERROR 182 #define TK_QNUMBER 183 #define TK_SPACE 184 -#define TK_ILLEGAL 185 +#define TK_COMMENT 185 +#define TK_ILLEGAL 186 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -15130,6 +15226,8 @@ typedef u64 tRowcnt; ** 0.5 -> -10 0.1 -> -33 0.0625 -> -40 */ typedef INT16_TYPE LogEst; +#define LOGEST_MIN (-32768) +#define LOGEST_MAX (32767) /* ** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer @@ -15400,7 +15498,7 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace; ** 0xFFFF---- Low-level debug messages ** ** 0x00000001 Code generation -** 0x00000002 Solver +** 0x00000002 Solver (Use 0x40000 for less detail) ** 0x00000004 Solver costs ** 0x00000008 WhereLoop inserts ** @@ -15419,6 +15517,8 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace; ** ** 0x00010000 Show more detail when printing WHERE terms ** 0x00020000 Show WHERE terms returned from whereScanNext() +** 0x00040000 Solver overview messages +** 0x00080000 Star-query heuristic */ @@ -16711,6 +16811,7 @@ typedef struct SubrtnSig SubrtnSig; */ struct SubrtnSig { int selId; /* SELECT-id for the SELECT statement on the RHS */ + u8 bComplete; /* True if fully coded and available for reusable */ char *zAff; /* Affinity of the overall IN expression */ int iTable; /* Ephemeral table generated by the subroutine */ int iAddr; /* Subroutine entry address */ @@ -18041,6 +18142,9 @@ struct sqlite3 { #define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */ #define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */ #define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */ +#define SQLITE_AttachCreate HI(0x00010) /* ATTACH allowed to create new dbs */ +#define SQLITE_AttachWrite HI(0x00020) /* ATTACH allowed to open for write */ +#define SQLITE_Comments HI(0x00040) /* Enable SQL comments */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG @@ -18100,6 +18204,7 @@ struct sqlite3 { #define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ #define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */ #define SQLITE_OrderBySubq 0x10000000 /* ORDER BY in subquery helps outer */ +#define SQLITE_StarQuery 0x20000000 /* Heurists for star queries */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* @@ -19429,13 +19534,8 @@ struct ExprList { */ struct IdList { int nId; /* Number of identifiers on the list */ - u8 eU4; /* Which element of a.u4 is valid */ struct IdList_item { char *zName; /* Name of the identifier */ - union { - int idx; /* Index in some Table.aCol[] of a column named zName */ - Expr *pExpr; /* Expr to implement a USING variable -- NOT USED */ - } u4; } a[1]; }; @@ -20122,9 +20222,7 @@ struct Parse { int nVtabLock; /* Number of virtual tables to lock */ #endif int nHeight; /* Expression tree height of current sub-select */ -#ifndef SQLITE_OMIT_EXPLAIN int addrExplain; /* Address of current OP_Explain opcode */ -#endif VList *pVList; /* Mapping between variable names and numbers */ Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ const char *zTail; /* All SQL text past the last semicolon parsed */ @@ -23582,6 +23680,7 @@ struct sqlite3_value { #ifdef SQLITE_DEBUG Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ u16 mScopyFlags; /* flags value immediately after the shallow copy */ + u8 bScopy; /* The pScopyFrom of some other Mem *might* point here */ #endif }; @@ -24680,6 +24779,9 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ zDate++; } ms /= rScale; + /* Truncate to avoid problems with sub-milliseconds + ** rounding. https://sqlite.org/forum/forumpost/766a2c9231 */ + if( ms>0.999 ) ms = 0.999; } }else{ s = 0; @@ -25887,7 +25989,7 @@ static void strftimeFunc( } case 'f': { /* Fractional seconds. (Non-standard) */ double s = x.s; - if( s>59.999 ) s = 59.999; + if( NEVER(s>59.999) ) s = 59.999; sqlite3_str_appendf(&sRes, "%06.3f", s); break; } @@ -33818,21 +33920,7 @@ SQLITE_PRIVATE void sqlite3TreeViewBareIdList( if( zName==0 ) zName = "(null)"; sqlite3TreeViewPush(&pView, moreToFollow); sqlite3TreeViewLine(pView, 0); - if( pList->eU4==EU4_NONE ){ - fprintf(stdout, "%s\n", zName); - }else if( pList->eU4==EU4_IDX ){ - fprintf(stdout, "%s (%d)\n", zName, pList->a[i].u4.idx); - }else{ - assert( pList->eU4==EU4_EXPR ); - if( pList->a[i].u4.pExpr==0 ){ - fprintf(stdout, "%s (pExpr=NULL)\n", zName); - }else{ - fprintf(stdout, "%s\n", zName); - sqlite3TreeViewPush(&pView, inId-1); - sqlite3TreeViewExpr(pView, pList->a[i].u4.pExpr, 0); - sqlite3TreeViewPop(&pView); - } - } + fprintf(stdout, "%s\n", zName); sqlite3TreeViewPop(&pView); } } @@ -40173,7 +40261,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){ if( pInode->bProcessLock==0 ){ struct flock lock; - assert( pInode->nLock==0 ); + /* assert( pInode->nLock==0 ); <-- Not true if unix-excl READONLY used */ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; @@ -58056,7 +58144,7 @@ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ if( pPager->pWal ){ u32 iRead = 0; (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); - return iRead==0; /* Condition (4) */ + if( iRead ) return 0; /* Case (4) */ } #endif assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 ); @@ -84004,27 +84092,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){ SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ int i; Mem *pX; - for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ - if( pX->pScopyFrom==pMem ){ - u16 mFlags; - if( pVdbe->db->flags & SQLITE_VdbeTrace ){ - sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", - (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); - } - /* If pX is marked as a shallow copy of pMem, then try to verify that - ** no significant changes have been made to pX since the OP_SCopy. - ** A significant change would indicated a missed call to this - ** function for pX. Minor changes, such as adding or removing a - ** dual type, are allowed, as long as the underlying value is the - ** same. */ - mFlags = pMem->flags & pX->flags & pX->mScopyFlags; - assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); - - /* pMem is the register that is changing. But also mark pX as - ** undefined so that we can quickly detect the shallow-copy error */ - pX->flags = MEM_Undefined; - pX->pScopyFrom = 0; - } + if( pMem->bScopy ){ + for(i=1, pX=pVdbe->aMem+1; inMem; i++, pX++){ + if( pX->pScopyFrom==pMem ){ + u16 mFlags; + if( pVdbe->db->flags & SQLITE_VdbeTrace ){ + sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n", + (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem)); + } + /* If pX is marked as a shallow copy of pMem, then try to verify that + ** no significant changes have been made to pX since the OP_SCopy. + ** A significant change would indicated a missed call to this + ** function for pX. Minor changes, such as adding or removing a + ** dual type, are allowed, as long as the underlying value is the + ** same. */ + mFlags = pMem->flags & pX->flags & pX->mScopyFlags; + assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i ); + + /* pMem is the register that is changing. But also mark pX as + ** undefined so that we can quickly detect the shallow-copy error */ + pX->flags = MEM_Undefined; + pX->pScopyFrom = 0; + } + } + pMem->bScopy = 0; } pMem->pScopyFrom = 0; } @@ -87166,6 +87257,7 @@ static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){ p->szMalloc = 0; #ifdef SQLITE_DEBUG p->pScopyFrom = 0; + p->bScopy = 0; #endif p++; }while( (--N)>0 ); @@ -90631,7 +90723,6 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){ sqlite3_int64 iNow; sqlite3_int64 iElapse; assert( p->startTime>0 ); - assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ); assert( db->init.busy==0 ); assert( p->zSql!=0 ); sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); @@ -91351,7 +91442,7 @@ static int sqlite3Step(Vdbe *p){ } assert( db->nVdbeWrite>0 || db->autoCommit==0 - || (db->nDeferredCons==0 && db->nDeferredImmCons==0) + || ((db->nDeferredCons + db->nDeferredImmCons)==0) ); #ifndef SQLITE_OMIT_TRACE @@ -91862,6 +91953,7 @@ static const Mem *columnNullValue(void){ #ifdef SQLITE_DEBUG /* .pScopyFrom = */ (Mem*)0, /* .mScopyFlags= */ 0, + /* .bScopy = */ 0, #endif }; return &nullMem; @@ -92744,6 +92836,7 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa PreUpdate *p; Mem *pMem; int rc = SQLITE_OK; + int iStore = 0; #ifdef SQLITE_ENABLE_API_ARMOR if( db==0 || ppValue==0 ){ @@ -92758,9 +92851,11 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa goto preupdate_old_out; } if( p->pPk ){ - iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx); + iStore = sqlite3TableColumnToIndex(p->pPk, iIdx); + }else{ + iStore = sqlite3TableColumnToStorage(p->pTab, iIdx); } - if( iIdx>=p->pCsr->nField || iIdx<0 ){ + if( iStore>=p->pCsr->nField || iStore<0 ){ rc = SQLITE_RANGE; goto preupdate_old_out; } @@ -92791,8 +92886,8 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa p->aRecord = aRec; } - pMem = *ppValue = &p->pUnpacked->aMem[iIdx]; - if( iIdx>=p->pUnpacked->nField ){ + pMem = *ppValue = &p->pUnpacked->aMem[iStore]; + if( iStore>=p->pUnpacked->nField ){ /* This occurs when the table has been extended using ALTER TABLE ** ADD COLUMN. The value to return is the default value of the column. */ Column *pCol = &p->pTab->aCol[iIdx]; @@ -92896,6 +92991,7 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa PreUpdate *p; int rc = SQLITE_OK; Mem *pMem; + int iStore = 0; #ifdef SQLITE_ENABLE_API_ARMOR if( db==0 || ppValue==0 ){ @@ -92908,9 +93004,12 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa goto preupdate_new_out; } if( p->pPk && p->op!=SQLITE_UPDATE ){ - iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx); + iStore = sqlite3TableColumnToIndex(p->pPk, iIdx); + }else{ + iStore = sqlite3TableColumnToStorage(p->pTab, iIdx); } - if( iIdx>=p->pCsr->nField || iIdx<0 ){ + + if( iStore>=p->pCsr->nField || iStore<0 ){ rc = SQLITE_RANGE; goto preupdate_new_out; } @@ -92930,14 +93029,14 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa } p->pNewUnpacked = pUnpack; } - pMem = &pUnpack->aMem[iIdx]; + pMem = &pUnpack->aMem[iStore]; if( iIdx==p->pTab->iPKey ){ sqlite3VdbeMemSetInt64(pMem, p->iKey2); - }else if( iIdx>=pUnpack->nField ){ + }else if( iStore>=pUnpack->nField ){ pMem = (sqlite3_value *)columnNullValue(); } }else{ - /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required + /* For an UPDATE, memory cell (p->iNewReg+1+iStore) contains the required ** value. Make a copy of the cell contents and return a pointer to it. ** It is not safe to return a pointer to the memory cell itself as the ** caller may modify the value text encoding. @@ -92950,13 +93049,13 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa goto preupdate_new_out; } } - assert( iIdx>=0 && iIdxpCsr->nField ); - pMem = &p->aNew[iIdx]; + assert( iStore>=0 && iStorepCsr->nField ); + pMem = &p->aNew[iStore]; if( pMem->flags==0 ){ if( iIdx==p->pTab->iPKey ){ sqlite3VdbeMemSetInt64(pMem, p->iKey2); }else{ - rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]); + rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iStore]); if( rc!=SQLITE_OK ) goto preupdate_new_out; } } @@ -94045,6 +94144,7 @@ static void registerTrace(int iReg, Mem *p){ printf("R[%d] = ", iReg); memTracePrint(p); if( p->pScopyFrom ){ + assert( p->pScopyFrom->bScopy ); printf(" <== R[%d]", (int)(p->pScopyFrom - &p[-iReg])); } printf("\n"); @@ -95028,6 +95128,7 @@ case OP_Move: { { int i; for(i=1; inMem; i++){ if( aMem[i].pScopyFrom==pIn1 ){ + assert( aMem[i].bScopy ); aMem[i].pScopyFrom = pOut; } } @@ -95100,6 +95201,7 @@ case OP_SCopy: { /* out2 */ #ifdef SQLITE_DEBUG pOut->pScopyFrom = pIn1; pOut->mScopyFlags = pIn1->flags; + pIn1->bScopy = 1; #endif break; } @@ -111363,16 +111465,13 @@ SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ int i; assert( db!=0 ); if( p==0 ) return 0; - assert( p->eU4!=EU4_EXPR ); pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) ); if( pNew==0 ) return 0; pNew->nId = p->nId; - pNew->eU4 = p->eU4; for(i=0; inId; i++){ struct IdList_item *pNewItem = &pNew->a[i]; const struct IdList_item *pOldItem = &p->a[i]; pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); - pNewItem->u4 = pOldItem->u4; } return pNew; } @@ -112896,6 +112995,7 @@ static int findCompatibleInRhsSubrtn( assert( pOp->opcode==OP_BeginSubrtn ); pSig = pOp->p4.pSubrtnSig; assert( pSig!=0 ); + if( !pSig->bComplete ) continue; if( pNewSig->selId!=pSig->selId ) continue; if( strcmp(pNewSig->zAff,pSig->zAff)!=0 ) continue; pExpr->y.sub.iAddr = pSig->iAddr; @@ -112942,6 +113042,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( KeyInfo *pKeyInfo = 0; /* Key information */ int nVal; /* Size of vector pLeft */ Vdbe *v; /* The prepared statement under construction */ + SubrtnSig *pSig = 0; /* Signature for this subroutine */ v = pParse->pVdbe; assert( v!=0 ); @@ -112962,7 +113063,6 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( ** Compute a signature for the RHS of the IN operator to facility ** finding and reusing prior instances of the same IN operator. */ - SubrtnSig *pSig = 0; assert( !ExprUseXSelect(pExpr) || pExpr->x.pSelect!=0 ); if( ExprUseXSelect(pExpr) && (pExpr->x.pSelect->selFlags & SF_All)==0 ){ pSig = sqlite3DbMallocRawNN(pParse->db, sizeof(pSig[0])); @@ -113005,6 +113105,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( pExpr->y.sub.iAddr = sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1; if( pSig ){ + pSig->bComplete = 0; pSig->iAddr = pExpr->y.sub.iAddr; pSig->regReturn = pExpr->y.sub.regReturn; pSig->iTable = iTab; @@ -113140,6 +113241,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( sqlite3ReleaseTempReg(pParse, r1); sqlite3ReleaseTempReg(pParse, r2); } + if( pSig ) pSig->bComplete = 1; if( pKeyInfo ){ sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); } @@ -121338,6 +121440,12 @@ static void attachFunc( sqlite3_free(zErr); return; } + if( (db->flags & SQLITE_AttachWrite)==0 ){ + flags &= ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE); + flags |= SQLITE_OPEN_READONLY; + }else if( (db->flags & SQLITE_AttachCreate)==0 ){ + flags &= ~SQLITE_OPEN_CREATE; + } assert( pVfs ); flags |= SQLITE_OPEN_MAIN_DB; rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); @@ -126729,7 +126837,6 @@ SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){ int i; assert( db!=0 ); if( pList==0 ) return; - assert( pList->eU4!=EU4_EXPR ); /* EU4_EXPR mode is not currently used */ for(i=0; inId; i++){ sqlite3DbFree(db, pList->a[i].zName); } @@ -128110,12 +128217,18 @@ static int matchQuality( u8 enc /* Desired text encoding */ ){ int match; - assert( p->nArg>=-1 ); + assert( p->nArg>=(-4) && p->nArg!=(-2) ); + assert( nArg>=(-2) ); /* Wrong number of arguments means "no match" */ if( p->nArg!=nArg ){ - if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH; + if( nArg==(-2) ) return p->xSFunc==0 ? 0 : FUNC_PERFECT_MATCH; if( p->nArg>=0 ) return 0; + /* Special p->nArg values available to built-in functions only: + ** -3 1 or more arguments required + ** -4 2 or more arguments required + */ + if( p->nArg<(-2) && nArg<(-2-p->nArg) ) return 0; } /* Give a better score to a function with a specific number of arguments @@ -131253,7 +131366,10 @@ static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){ assert( p->cnt>0 ); p->cnt--; if( !p->approx ){ - p->iSum -= sqlite3_value_int64(argv[0]); + if( sqlite3SubInt64(&p->iSum, sqlite3_value_int64(argv[0])) ){ + p->ovrfl = 1; + p->approx = 1; + } }else if( type==SQLITE_INTEGER ){ i64 iVal = sqlite3_value_int64(argv[0]); if( iVal!=SMALLEST_INT64 ){ @@ -132079,12 +132195,10 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(rtrim, 2, 2, 0, trimFunc ), FUNCTION(trim, 1, 3, 0, trimFunc ), FUNCTION(trim, 2, 3, 0, trimFunc ), - FUNCTION(min, -1, 0, 1, minmaxFunc ), - FUNCTION(min, 0, 0, 1, 0 ), + FUNCTION(min, -3, 0, 1, minmaxFunc ), WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), - FUNCTION(max, -1, 1, 1, minmaxFunc ), - FUNCTION(max, 0, 1, 1, 0 ), + FUNCTION(max, -3, 1, 1, minmaxFunc ), WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0, SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ), FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF), @@ -132111,11 +132225,8 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(hex, 1, 0, 0, hexFunc ), FUNCTION(unhex, 1, 0, 0, unhexFunc ), FUNCTION(unhex, 2, 0, 0, unhexFunc ), - FUNCTION(concat, -1, 0, 0, concatFunc ), - FUNCTION(concat, 0, 0, 0, 0 ), - FUNCTION(concat_ws, -1, 0, 0, concatwsFunc ), - FUNCTION(concat_ws, 0, 0, 0, 0 ), - FUNCTION(concat_ws, 1, 0, 0, 0 ), + FUNCTION(concat, -3, 0, 0, concatFunc ), + FUNCTION(concat_ws, -4, 0, 0, concatwsFunc ), INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ), VFUNCTION(random, 0, 0, 0, randomFunc ), VFUNCTION(randomblob, 1, 0, 0, randomBlob ), @@ -132159,8 +132270,6 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){ #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION FUNCTION(unknown, -1, 0, 0, unknownFunc ), #endif - FUNCTION(coalesce, 1, 0, 0, 0 ), - FUNCTION(coalesce, 0, 0, 0, 0 ), #ifdef SQLITE_ENABLE_MATH_FUNCTIONS MFUNCTION(ceil, 1, xCeil, ceilingFunc ), MFUNCTION(ceiling, 1, xCeil, ceilingFunc ), @@ -132198,11 +132307,9 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){ MFUNCTION(pi, 0, 0, piFunc ), #endif /* SQLITE_ENABLE_MATH_FUNCTIONS */ FUNCTION(sign, 1, 0, 0, signFunc ), - INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ), - INLINE_FUNC(iif, 2, INLINEFUNC_iif, 0 ), - INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ), - INLINE_FUNC(if, 2, INLINEFUNC_iif, 0 ), - INLINE_FUNC(if, 3, INLINEFUNC_iif, 0 ), + INLINE_FUNC(coalesce, -4, INLINEFUNC_coalesce, 0 ), + INLINE_FUNC(iif, -4, INLINEFUNC_iif, 0 ), + INLINE_FUNC(if, -4, INLINEFUNC_iif, 0 ), }; #ifndef SQLITE_OMIT_ALTERTABLE sqlite3AlterFunctions(); @@ -134648,6 +134755,7 @@ SQLITE_PRIVATE void sqlite3Insert( int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ int *aRegIdx = 0; /* One register allocated to each index */ + int *aTabColMap = 0; /* Mapping from pTab columns to pCol entries */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ @@ -134792,15 +134900,15 @@ SQLITE_PRIVATE void sqlite3Insert( */ bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0; if( pColumn ){ - assert( pColumn->eU4!=EU4_EXPR ); - pColumn->eU4 = EU4_IDX; - for(i=0; inId; i++){ - pColumn->a[i].u4.idx = -1; - } + aTabColMap = sqlite3DbMallocZero(db, pTab->nCol*sizeof(int)); + if( aTabColMap==0 ) goto insert_cleanup; for(i=0; inId; i++){ + const char *zCName = pColumn->a[i].zName; + u8 hName = sqlite3StrIHash(zCName); for(j=0; jnCol; j++){ - if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zCnName)==0 ){ - pColumn->a[i].u4.idx = j; + if( pTab->aCol[j].hName!=hName ) continue; + if( sqlite3StrICmp(zCName, pTab->aCol[j].zCnName)==0 ){ + if( aTabColMap[j]==0 ) aTabColMap[j] = i+1; if( i!=j ) bIdListInOrder = 0; if( j==pTab->iPKey ){ ipkColumn = i; assert( !withoutRowid ); @@ -135122,9 +135230,9 @@ SQLITE_PRIVATE void sqlite3Insert( } } if( pColumn ){ - assert( pColumn->eU4==EU4_IDX ); - for(j=0; jnId && pColumn->a[j].u4.idx!=i; j++){} - if( j>=pColumn->nId ){ + j = aTabColMap[i]; + assert( j>=0 && j<=pColumn->nId ); + if( j==0 ){ /* A column not named in the insert column list gets its ** default value */ sqlite3ExprCodeFactorable(pParse, @@ -135132,7 +135240,7 @@ SQLITE_PRIVATE void sqlite3Insert( iRegStore); continue; } - k = j; + k = j - 1; }else if( nColumn==0 ){ /* This is INSERT INTO ... DEFAULT VALUES. Load the default value. */ sqlite3ExprCodeFactorable(pParse, @@ -135377,7 +135485,10 @@ SQLITE_PRIVATE void sqlite3Insert( sqlite3ExprListDelete(db, pList); sqlite3UpsertDelete(db, pUpsert); sqlite3SelectDelete(db, pSelect); - sqlite3IdListDelete(db, pColumn); + if( pColumn ){ + sqlite3IdListDelete(db, pColumn); + sqlite3DbFree(db, aTabColMap); + } if( aRegIdx ) sqlite3DbNNFreeNN(db, aRegIdx); } @@ -157233,7 +157344,9 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ z = (const unsigned char*)zCreateTable; for(i=0; aKeyword[i]; i++){ int tokenType = 0; - do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE ); + do{ + z += sqlite3GetToken(z, &tokenType); + }while( tokenType==TK_SPACE || tokenType==TK_COMMENT ); if( tokenType!=aKeyword[i] ){ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error"); return SQLITE_ERROR; @@ -157964,8 +158077,10 @@ struct WhereLoop { /**** whereLoopXfer() copies fields above ***********************/ # define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot) u16 nLSlot; /* Number of slots allocated for aLTerm[] */ +#ifdef WHERETRACE_ENABLED LogEst rStarDelta; /* Cost delta due to star-schema heuristic. Not - ** initialized unless pWInfo->nOutStarDelta>0 */ + ** initialized unless pWInfo->bStarUsed */ +#endif WhereTerm **aLTerm; /* WhereTerms used */ WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */ WhereTerm *aLTermSpace[3]; /* Initial aLTerm[] space */ @@ -158014,7 +158129,7 @@ struct WherePath { Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */ LogEst nRow; /* Estimated number of rows generated by this path */ LogEst rCost; /* Total cost of this path */ - LogEst rUnsorted; /* Total cost of this path ignoring sorting costs */ + LogEst rUnsort; /* Total cost of this path ignoring sorting costs */ i8 isOrdered; /* No. of ORDER BY terms satisfied. -1 for unknown */ WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */ }; @@ -158287,9 +158402,13 @@ struct WhereInfo { unsigned bDeferredSeek :1; /* Uses OP_DeferredSeek */ unsigned untestedTerms :1; /* Not all WHERE terms resolved by outer loop */ unsigned bOrderedInnerLoop:1;/* True if only the inner-most loop is ordered */ - unsigned sorted :1; /* True if really sorted (not just grouped) */ - LogEst nOutStarDelta; /* Artifical nOut reduction for star-query */ + unsigned sorted :1; /* True if really sorted (not just grouped) */ + unsigned bStarDone :1; /* True if check for star-query is complete */ + unsigned bStarUsed :1; /* True if star-query heuristic is used */ LogEst nRowOut; /* Estimated number of output rows */ +#ifdef WHERETRACE_ENABLED + LogEst rTotalCost; /* Total cost of the solution */ +#endif int iTop; /* The very beginning of the WHERE loop */ int iEndWhere; /* End of the WHERE clause itself */ WhereLoop *pLoops; /* List of all WhereLoop objects */ @@ -161600,12 +161719,12 @@ static int isLikeOrGlob( z = (u8*)pRight->u.zToken; } if( z ){ - /* Count the number of prefix bytes prior to the first wildcard. - ** or U+fffd character. If the underlying database has a UTF16LE - ** encoding, then only consider ASCII characters. Note that the - ** encoding of z[] is UTF8 - we are dealing with only UTF8 here in - ** this code, but the database engine itself might be processing - ** content using a different encoding. */ + /* Count the number of prefix bytes prior to the first wildcard, + ** U+fffd character, or malformed utf-8. If the underlying database + ** has a UTF16LE encoding, then only consider ASCII characters. Note that + ** the encoding of z[] is UTF8 - we are dealing with only UTF8 here in this + ** code, but the database engine itself might be processing content using a + ** different encoding. */ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; @@ -161613,7 +161732,9 @@ static int isLikeOrGlob( cnt++; }else if( c>=0x80 ){ const u8 *z2 = z+cnt-1; - if( sqlite3Utf8Read(&z2)==0xfffd || ENC(db)==SQLITE_UTF16LE ){ + if( c==0xff || sqlite3Utf8Read(&z2)==0xfffd /* bad utf-8 */ + || ENC(db)==SQLITE_UTF16LE + ){ cnt--; break; }else{ @@ -162765,9 +162886,8 @@ static void exprAnalyze( } if( !db->mallocFailed ){ - u8 c, *pC; /* Last character before the first wildcard */ + u8 *pC; /* Last character before the first wildcard */ pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; - c = *pC; if( noCase ){ /* The point is to increment the last character before the first ** wildcard. But if we increment '@', that will push it into the @@ -162775,10 +162895,17 @@ static void exprAnalyze( ** inequality. To avoid this, make sure to also run the full ** LIKE on all candidate expressions by clearing the isComplete flag */ - if( c=='A'-1 ) isComplete = 0; - c = sqlite3UpperToLower[c]; + if( *pC=='A'-1 ) isComplete = 0; + *pC = sqlite3UpperToLower[*pC]; + } + + /* Increment the value of the last utf8 character in the prefix. */ + while( *pC==0xBF && pC>(u8*)pStr2->u.zToken ){ + *pC = 0x80; + pC--; } - *pC = c + 1; + assert( *pC!=0xFF ); /* isLikeOrGlob() guarantees this */ + (*pC)++; } zCollSeqName = noCase ? "NOCASE" : sqlite3StrBINARY; pNewExpr1 = sqlite3ExprDup(db, pLeft, 0); @@ -164235,7 +164362,7 @@ static void explainAutomaticIndex( sqlite3_str *pStr = sqlite3_str_new(pParse->db); sqlite3_str_appendf(pStr,"CREATE AUTOMATIC INDEX ON %s(", pTab->zName); assert( pIdx->nColumn>1 ); - assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID ); + assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID || !HasRowid(pTab) ); for(ii=0; ii<(pIdx->nColumn-1); ii++){ const char *zName = 0; int iCol = pIdx->aiColumn[ii]; @@ -164366,6 +164493,19 @@ static SQLITE_NOINLINE void constructAutomaticIndex( }else{ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); } + if( !HasRowid(pTable) ){ + /* For WITHOUT ROWID tables, ensure that all PRIMARY KEY columns are + ** either in the idxCols mask or in the extraCols mask */ + for(i=0; inCol; i++){ + if( (pTable->aCol[i].colFlags & COLFLAG_PRIMKEY)==0 ) continue; + if( i>=BMS-1 ){ + extraCols |= MASKBIT(BMS-1); + break; + } + if( idxCols & MASKBIT(i) ) continue; + extraCols |= MASKBIT(i); + } + } mxBitCol = MIN(BMS-1,pTable->nCol); testcase( pTable->nCol==BMS-1 ); testcase( pTable->nCol==BMS-2 ); @@ -164377,7 +164517,8 @@ static SQLITE_NOINLINE void constructAutomaticIndex( } /* Construct the Index object to describe this index */ - pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed); + pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable), + 0, &zNotUsed); if( pIdx==0 ) goto end_auto_index_create; pLoop->u.btree.pIndex = pIdx; pIdx->zName = "auto-index"; @@ -164433,8 +164574,10 @@ static SQLITE_NOINLINE void constructAutomaticIndex( } } assert( n==nKeyCol ); - pIdx->aiColumn[n] = XN_ROWID; - pIdx->azColl[n] = sqlite3StrBINARY; + if( HasRowid(pTable) ){ + pIdx->aiColumn[n] = XN_ROWID; + pIdx->azColl[n] = sqlite3StrBINARY; + } /* Create the automatic index */ explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp); @@ -165701,8 +165844,9 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){ ** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31 */ SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ + WhereInfo *pWInfo; if( pWC ){ - WhereInfo *pWInfo = pWC->pWInfo; + pWInfo = pWC->pWInfo; int nb = 1+(pWInfo->pTabList->nSrc+3)/4; SrcItem *pItem = pWInfo->pTabList->a + p->iTab; Table *pTab = pItem->pSTab; @@ -165712,6 +165856,7 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause sqlite3DebugPrintf(" %12s", pItem->zAlias ? pItem->zAlias : pTab->zName); }else{ + pWInfo = 0; sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d", p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab); } @@ -165743,7 +165888,12 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause }else{ sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm); } - sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); + if( pWInfo && pWInfo->bStarUsed && p->rStarDelta!=0 ){ + sqlite3DebugPrintf(" cost %d,%d,%d delta=%d\n", + p->rSetup, p->rRun, p->nOut, p->rStarDelta); + }else{ + sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); + } if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){ int i; for(i=0; inLTerm; i++){ @@ -167209,7 +167359,6 @@ static int whereLoopAddBtree( && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 && !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ - && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */ && !pSrc->fg.isCorrelated /* Not a correlated subquery */ && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ && (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */ @@ -168712,68 +168861,201 @@ static LogEst whereSortingCost( ** 18 for star queries ** 12 otherwise ** -** For the purposes of SQLite, a star-query is defined as a query -** with a large central table that is joined against four or more -** smaller tables. The central table is called the "fact" table. -** The smaller tables that get joined are "dimension tables". +** For the purposes of this heuristic, a star-query is defined as a query +** with a large central table that is joined using an INNER JOIN, +** not CROSS or OUTER JOINs, against four or more smaller tables. +** The central table is called the "fact" table. The smaller tables +** that get joined are "dimension tables". Also, any table that is +** self-joined cannot be a dimension table; we assume that dimension +** tables may only be joined against fact tables. ** ** SIDE EFFECT: (and really the whole point of this subroutine) ** -** If pWInfo describes a star-query, then the cost on WhereLoops for the -** fact table is reduced. This heuristic helps keep fact tables in -** outer loops. Without this heuristic, paths with fact tables in outer -** loops tend to get pruned by the mxChoice limit on the number of paths, -** resulting in poor query plans. The total amount of heuristic cost -** adjustment is stored in pWInfo->nOutStarDelta and the cost adjustment -** for each WhereLoop is stored in its rStarDelta field. +** If pWInfo describes a star-query, then the cost for SCANs of dimension +** WhereLoops is increased to be slightly larger than the cost of a SCAN +** in the fact table. Only SCAN costs are increased. SEARCH costs are +** unchanged. This heuristic helps keep fact tables in outer loops. Without +** this heuristic, paths with fact tables in outer loops tend to get pruned +** by the mxChoice limit on the number of paths, resulting in poor query +** plans. See the starschema1.test test module for examples of queries +** that need this heuristic to find good query plans. +** +** This heuristic can be completely disabled, so that no query is +** considered a star-query, using SQLITE_TESTCTRL_OPTIMIZATION to +** disable the SQLITE_StarQuery optimization. In the CLI, the command +** to do that is: ".testctrl opt -starquery". +** +** HISTORICAL NOTES: +** +** This optimization was first added on 2024-05-09 by check-in 38db9b5c83d. +** The original optimization reduced the cost and output size estimate for +** fact tables to help them move to outer loops. But months later (as people +** started upgrading) performance regression reports started caming in, +** including: +** +** forum post b18ef983e68d06d1 (2024-12-21) +** forum post 0025389d0860af82 (2025-01-14) +** forum post d87570a145599033 (2025-01-17) +** +** To address these, the criteria for a star-query was tightened to exclude +** cases where the fact and dimensions are separated by an outer join, and +** the affect of star-schema detection was changed to increase the rRun cost +** on just full table scans of dimension tables, rather than reducing costs +** in the all access methods of the fact table. */ -static int computeMxChoice(WhereInfo *pWInfo, LogEst nRowEst){ +static int computeMxChoice(WhereInfo *pWInfo){ int nLoop = pWInfo->nLevel; /* Number of terms in the join */ - if( nRowEst==0 && nLoop>=5 ){ - /* Check to see if we are dealing with a star schema and if so, reduce - ** the cost of fact tables relative to dimension tables, as a heuristic - ** to help keep the fact tables in outer loops. + WhereLoop *pWLoop; /* For looping over WhereLoops */ + +#ifdef SQLITE_DEBUG + /* The star-query detection code below makes use of the following + ** properties of the WhereLoop list, so verify them before + ** continuing: + ** (1) .maskSelf is the bitmask corresponding to .iTab + ** (2) The WhereLoop list is in ascending .iTab order + */ + for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ + assert( pWLoop->maskSelf==MASKBIT(pWLoop->iTab) ); + assert( pWLoop->pNextLoop==0 || pWLoop->iTab<=pWLoop->pNextLoop->iTab ); + } +#endif /* SQLITE_DEBUG */ + + if( nLoop>=5 + && !pWInfo->bStarDone + && OptimizationEnabled(pWInfo->pParse->db, SQLITE_StarQuery) + ){ + SrcItem *aFromTabs; /* All terms of the FROM clause */ + int iFromIdx; /* Term of FROM clause is the candidate fact-table */ + Bitmask m; /* Bitmask for candidate fact-table */ + Bitmask mSelfJoin = 0; /* Tables that cannot be dimension tables */ + WhereLoop *pStart; /* Where to start searching for dimension-tables */ + + pWInfo->bStarDone = 1; /* Only do this computation once */ + + /* Look for fact tables with four or more dimensions where the + ** dimension tables are not separately from the fact tables by an outer + ** or cross join. Adjust cost weights if found. */ - int iLoop; /* Counter over join terms */ - Bitmask m; /* Bitmask for current loop */ - assert( pWInfo->nOutStarDelta==0 ); - for(iLoop=0, m=1; iLoopbStarUsed ); + aFromTabs = pWInfo->pTabList->a; + pStart = pWInfo->pLoops; + for(iFromIdx=0, m=1; iFromIdxpLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ - if( (pWLoop->prereq & m)!=0 && (pWLoop->maskSelf & mSeen)==0 ){ - nDep++; - mSeen |= pWLoop->maskSelf; + SrcItem *pFactTab; /* The candidate fact table */ + + pFactTab = aFromTabs + iFromIdx; + if( (pFactTab->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ + /* If the candidate fact-table is the right table of an outer join + ** restrict the search for dimension-tables to be tables to the right + ** of the fact-table. */ + if( iFromIdx+4 > nLoop ) break; /* Impossible to reach nDep>=4 */ + while( pStart && pStart->iTab<=iFromIdx ){ + pStart = pStart->pNextLoop; + } + } + for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ + if( (aFromTabs[pWLoop->iTab].fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ + /* Fact-tables and dimension-tables cannot be separated by an + ** outer join (at least for the definition of fact- and dimension- + ** used by this heuristic). */ + break; + } + if( (pWLoop->prereq & m)!=0 /* pWInfo depends on iFromIdx */ + && (pWLoop->maskSelf & mSeen)==0 /* pWInfo not already a dependency */ + && (pWLoop->maskSelf & mSelfJoin)==0 /* Not a self-join */ + ){ + if( aFromTabs[pWLoop->iTab].pSTab==pFactTab->pSTab ){ + mSelfJoin |= m; + }else{ + nDep++; + mSeen |= pWLoop->maskSelf; + } } } if( nDep<=3 ) continue; - rDelta = 15*(nDep-3); -#ifdef WHERETRACE_ENABLED /* 0x4 */ - if( sqlite3WhereTrace&0x4 ){ - SrcItem *pItem = pWInfo->pTabList->a + iLoop; - sqlite3DebugPrintf("Fact-table %s: %d dimensions, cost reduced %d\n", - pItem->zAlias ? pItem->zAlias : pItem->pSTab->zName, - nDep, rDelta); - } -#endif - if( pWInfo->nOutStarDelta==0 ){ + + /* If we reach this point, it means that pFactTab is a fact table + ** with four or more dimensions connected by inner joins. Proceed + ** to make cost adjustments. */ + +#ifdef WHERETRACE_ENABLED + /* Make sure rStarDelta values are initialized */ + if( !pWInfo->bStarUsed ){ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ pWLoop->rStarDelta = 0; } } - pWInfo->nOutStarDelta += rDelta; +#endif + pWInfo->bStarUsed = 1; + + /* Compute the maximum cost of any WhereLoop for the + ** fact table plus one epsilon */ + mxRun = LOGEST_MIN; + for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ + if( pWLoop->iTabiTab>iFromIdx ) break; + if( pWLoop->rRun>mxRun ) mxRun = pWLoop->rRun; + } + if( ALWAYS(mxRunpNextLoop){ + if( (pWLoop->maskSelf & mSeen)==0 ) continue; + if( pWLoop->nLTerm ) continue; + if( pWLoop->rRuniTab; + sqlite3DebugPrintf( + "Increase SCAN cost of dimension %s(%d) of fact %s(%d) to %d\n", + pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, pWLoop->iTab, + pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName, + iFromIdx, mxRun + ); + } + pWLoop->rStarDelta = mxRun - pWLoop->rRun; +#endif /* WHERETRACE_ENABLED */ + pWLoop->rRun = mxRun; + } + } + } +#ifdef WHERETRACE_ENABLED /* 0x80000 */ + if( (sqlite3WhereTrace & 0x80000)!=0 && pWInfo->bStarUsed ){ + sqlite3DebugPrintf("WhereLoops changed by star-query heuristic:\n"); for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ - if( pWLoop->maskSelf==m ){ - pWLoop->rRun -= rDelta; - pWLoop->nOut -= rDelta; - pWLoop->rStarDelta = rDelta; + if( pWLoop->rStarDelta ){ + sqlite3WhereLoopPrint(pWLoop, &pWInfo->sWC); } } } +#endif } - return pWInfo->nOutStarDelta>0 ? 18 : 12; + return pWInfo->bStarUsed ? 18 : 12; +} + +/* +** Two WhereLoop objects, pCandidate and pBaseline, are known to have the +** same cost. Look deep into each to see if pCandidate is even slightly +** better than pBaseline. Return false if it is, if pCandidate is is preferred. +** Return true if pBaseline is preferred or if we cannot tell the difference. +** +** Result Meaning +** -------- ---------------------------------------------------------- +** true We cannot tell the difference in pCandidate and pBaseline +** false pCandidate seems like a better choice than pBaseline +*/ +static SQLITE_NOINLINE int whereLoopIsNoBetter( + const WhereLoop *pCandidate, + const WhereLoop *pBaseline +){ + if( (pCandidate->wsFlags & WHERE_INDEXED)==0 ) return 1; + if( (pBaseline->wsFlags & WHERE_INDEXED)==0 ) return 1; + if( pCandidate->u.btree.pIndex->szIdxRow < + pBaseline->u.btree.pIndex->szIdxRow ) return 0; + return 1; } /* @@ -168797,7 +169079,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ int mxI = 0; /* Index of next entry to replace */ int nOrderBy; /* Number of ORDER BY clause terms */ LogEst mxCost = 0; /* Maximum cost of a set of paths */ - LogEst mxUnsorted = 0; /* Maximum unsorted cost of a set of path */ + LogEst mxUnsort = 0; /* Maximum unsorted cost of a set of path */ int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */ WherePath *aFrom; /* All nFrom paths at the previous level */ WherePath *aTo; /* The nTo best paths at the current level */ @@ -168826,8 +169108,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ mxChoice = 1; }else if( nLoop==2 ){ mxChoice = 5; + }else if( pParse->nErr ){ + mxChoice = 1; }else{ - mxChoice = computeMxChoice(pWInfo, nRowEst); + mxChoice = computeMxChoice(pWInfo); } assert( nLoop<=pWInfo->pTabList->nSrc ); @@ -168894,7 +169178,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ LogEst nOut; /* Rows visited by (pFrom+pWLoop) */ LogEst rCost; /* Cost of path (pFrom+pWLoop) */ - LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */ + LogEst rUnsort; /* Unsorted cost of (pFrom+pWLoop) */ i8 isOrdered; /* isOrdered for (pFrom+pWLoop) */ Bitmask maskNew; /* Mask of src visited by (..) */ Bitmask revMask; /* Mask of rev-order loops for (..) */ @@ -168912,11 +169196,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ /* At this point, pWLoop is a candidate to be the next loop. ** Compute its cost */ - rUnsorted = pWLoop->rRun + pFrom->nRow; + rUnsort = pWLoop->rRun + pFrom->nRow; if( pWLoop->rSetup ){ - rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup, rUnsorted); + rUnsort = sqlite3LogEstAdd(pWLoop->rSetup, rUnsort); } - rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted); + rUnsort = sqlite3LogEstAdd(rUnsort, pFrom->rUnsort); nOut = pFrom->nRow + pWLoop->nOut; maskNew = pFrom->maskLoop | pWLoop->maskSelf; isOrdered = pFrom->isOrdered; @@ -168938,15 +169222,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ ** extra encouragement to the query planner to select a plan ** where the rows emerge in the correct order without any sorting ** required. */ - rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 3; + rCost = sqlite3LogEstAdd(rUnsort, aSortCost[isOrdered]) + 3; WHERETRACE(0x002, ("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n", aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy, - rUnsorted, rCost)); + rUnsort, rCost)); }else{ - rCost = rUnsorted; - rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */ + rCost = rUnsort; + rUnsort -= 2; /* TUNING: Slight bias in favor of no-sort plans */ } /* Check to see if pWLoop should be added to the set of @@ -168972,7 +169256,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( jj>=nTo ){ /* None of the existing best-so-far paths match the candidate. */ if( nTo>=mxChoice - && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted)) + && (rCost>mxCost || (rCost==mxCost && rUnsort>=mxUnsort)) ){ /* The current candidate is no better than any of the mxChoice ** paths currently in the best-so-far buffer. So discard @@ -168980,7 +169264,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf("Skip %s cost=%-3d,%3d,%3d order=%c\n", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, + wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, isOrdered>=0 ? isOrdered+'0' : '?'); } #endif @@ -168999,7 +169283,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf("New %s cost=%-3d,%3d,%3d order=%c\n", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, + wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, isOrdered>=0 ? isOrdered+'0' : '?'); } #endif @@ -169010,24 +169294,23 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ ** pTo or if the candidate should be skipped. ** ** The conditional is an expanded vector comparison equivalent to: - ** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted) + ** (pTo->rCost,pTo->nRow,pTo->rUnsort) <= (rCost,nOut,rUnsort) */ - if( pTo->rCostrCost==rCost - && (pTo->nRownRow==nOut && pTo->rUnsorted<=rUnsorted) - ) - ) + if( (pTo->rCostrCost==rCost && pTo->nRowrCost==rCost && pTo->nRow==nOut && pTo->rUnsortrCost==rCost && pTo->nRow==nOut && pTo->rUnsort==rUnsort + && whereLoopIsNoBetter(pWLoop, pTo->aLoop[iLoop]) ) ){ #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf( "Skip %s cost=%-3d,%3d,%3d order=%c", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, + wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, isOrdered>=0 ? isOrdered+'0' : '?'); sqlite3DebugPrintf(" vs %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, - pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); + pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); } #endif /* Discard the candidate path from further consideration */ @@ -169041,11 +169324,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( sqlite3WhereTrace&0x4 ){ sqlite3DebugPrintf( "Update %s cost=%-3d,%3d,%3d order=%c", - wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, + wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort, isOrdered>=0 ? isOrdered+'0' : '?'); sqlite3DebugPrintf(" was %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, - pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); + pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); } #endif } @@ -169054,20 +169337,20 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ pTo->revLoop = revMask; pTo->nRow = nOut; pTo->rCost = rCost; - pTo->rUnsorted = rUnsorted; + pTo->rUnsort = rUnsort; pTo->isOrdered = isOrdered; memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop); pTo->aLoop[iLoop] = pWLoop; if( nTo>=mxChoice ){ mxI = 0; mxCost = aTo[0].rCost; - mxUnsorted = aTo[0].nRow; + mxUnsort = aTo[0].nRow; for(jj=1, pTo=&aTo[1]; jjrCost>mxCost - || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted) + || (pTo->rCost==mxCost && pTo->rUnsort>mxUnsort) ){ mxCost = pTo->rCost; - mxUnsorted = pTo->rUnsorted; + mxUnsort = pTo->rUnsort; mxI = jj; } } @@ -169079,8 +169362,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( sqlite3WhereTrace & 0x02 ){ LogEst rMin, rFloor = 0; int nDone = 0; + int nProgress; sqlite3DebugPrintf("---- after round %d ----\n", iLoop); - while( nDonerCost>rFloor && pTo->rCostrCost; @@ -169096,10 +169381,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ sqlite3DebugPrintf("\n"); } nDone++; + nProgress++; } } rFloor = rMin; - } + }while( nDone0 ); } #endif @@ -169193,7 +169479,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } } - pWInfo->nRowOut = pFrom->nRow + pWInfo->nOutStarDelta; + pWInfo->nRowOut = pFrom->nRow; +#ifdef WHERETRACE_ENABLED + pWInfo->rTotalCost = pFrom->rCost; +#endif /* Free temporary memory and return success */ sqlite3StackFreeNN(pParse->db, pSpace); @@ -169591,7 +169880,6 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( } } nSearch += pLoop->nOut; - if( pWInfo->nOutStarDelta ) nSearch += pLoop->rStarDelta; } } @@ -170074,7 +170362,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( assert( db->mallocFailed==0 ); #ifdef WHERETRACE_ENABLED if( sqlite3WhereTrace ){ - sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); + sqlite3DebugPrintf("---- Solution cost=%d, nRow=%d", + pWInfo->rTotalCost, pWInfo->nRowOut); if( pWInfo->nOBSat>0 ){ sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask); } @@ -174332,7 +174621,8 @@ static void updateDeleteLimitError( #define TK_ERROR 182 #define TK_QNUMBER 183 #define TK_SPACE 184 -#define TK_ILLEGAL 185 +#define TK_COMMENT 185 +#define TK_ILLEGAL 186 #endif /**************** End token definitions ***************************************/ @@ -174397,31 +174687,31 @@ static void updateDeleteLimitError( #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 322 +#define YYNOCODE 323 #define YYACTIONTYPE unsigned short int #define YYWILDCARD 102 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlite3ParserTOKENTYPE yy0; - ExprList* yy14; - With* yy59; - Cte* yy67; - Upsert* yy122; - IdList* yy132; - int yy144; - const char* yy168; - SrcList* yy203; - Window* yy211; - OnOrUsing yy269; - struct TrigEvent yy286; - struct {int value; int mask;} yy383; - u32 yy391; - TriggerStep* yy427; - Expr* yy454; - u8 yy462; - struct FrameBound yy509; - Select* yy555; + u32 yy9; + struct TrigEvent yy28; + With* yy125; + IdList* yy204; + struct FrameBound yy205; + TriggerStep* yy319; + const char* yy342; + Cte* yy361; + ExprList* yy402; + Upsert* yy403; + OnOrUsing yy421; + u8 yy444; + struct {int value; int mask;} yy481; + Window* yy483; + int yy502; + SrcList* yy563; + Expr* yy590; + Select* yy637; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -174443,7 +174733,7 @@ typedef union { #define YYNSTATE 583 #define YYNRULE 409 #define YYNRULE_WITH_ACTION 344 -#define YYNTOKEN 186 +#define YYNTOKEN 187 #define YY_MAX_SHIFT 582 #define YY_MIN_SHIFTREDUCE 845 #define YY_MAX_SHIFTREDUCE 1253 @@ -174452,8 +174742,8 @@ typedef union { #define YY_NO_ACTION 1256 #define YY_MIN_REDUCE 1257 #define YY_MAX_REDUCE 1665 -#define YY_MIN_DSTRCTR 205 -#define YY_MAX_DSTRCTR 319 +#define YY_MIN_DSTRCTR 206 +#define YY_MAX_DSTRCTR 320 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -174549,211 +174839,211 @@ static const YYACTIONTYPE yy_action[] = { /* 80 */ 573, 421, 562, 137, 138, 91, 559, 1228, 1228, 1063, /* 90 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 296, /* 100 */ 460, 398, 1249, 134, 134, 134, 134, 133, 133, 132, - /* 110 */ 132, 132, 131, 128, 451, 44, 1050, 1050, 1064, 1067, + /* 110 */ 132, 132, 131, 128, 451, 451, 1050, 1050, 1064, 1067, /* 120 */ 1255, 1, 1, 582, 2, 1259, 581, 1174, 1259, 1174, - /* 130 */ 321, 413, 155, 321, 1584, 155, 379, 112, 498, 1341, + /* 130 */ 321, 413, 155, 321, 1584, 155, 379, 112, 481, 1341, /* 140 */ 456, 299, 1341, 134, 134, 134, 134, 133, 133, 132, - /* 150 */ 132, 132, 131, 128, 451, 137, 138, 91, 1105, 1228, + /* 150 */ 132, 132, 131, 128, 451, 137, 138, 91, 498, 1228, /* 160 */ 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, - /* 170 */ 136, 1204, 320, 567, 288, 288, 283, 288, 288, 523, - /* 180 */ 523, 1250, 139, 1541, 7, 214, 503, 573, 1169, 562, - /* 190 */ 573, 1054, 562, 136, 136, 136, 136, 129, 401, 547, - /* 200 */ 487, 1169, 245, 1568, 1169, 245, 133, 133, 132, 132, - /* 210 */ 132, 131, 128, 451, 261, 134, 134, 134, 134, 133, - /* 220 */ 133, 132, 132, 132, 131, 128, 451, 451, 1204, 1205, - /* 230 */ 1204, 130, 127, 234, 455, 413, 182, 455, 130, 127, + /* 170 */ 136, 1204, 862, 1281, 288, 288, 283, 288, 288, 523, + /* 180 */ 523, 1250, 139, 578, 7, 578, 1345, 573, 1169, 562, + /* 190 */ 573, 1054, 562, 136, 136, 136, 136, 129, 573, 547, + /* 200 */ 562, 1169, 245, 1541, 1169, 245, 133, 133, 132, 132, + /* 210 */ 132, 131, 128, 451, 302, 134, 134, 134, 134, 133, + /* 220 */ 133, 132, 132, 132, 131, 128, 451, 1575, 1204, 1205, + /* 230 */ 1204, 7, 470, 550, 455, 413, 550, 455, 130, 127, /* 240 */ 234, 134, 134, 134, 134, 133, 133, 132, 132, 132, - /* 250 */ 131, 128, 451, 136, 136, 136, 136, 538, 576, 137, - /* 260 */ 138, 91, 261, 1228, 1228, 1063, 1066, 1053, 1053, 135, - /* 270 */ 135, 136, 136, 136, 136, 44, 472, 346, 1204, 472, - /* 280 */ 346, 51, 51, 418, 93, 157, 134, 134, 134, 134, - /* 290 */ 133, 133, 132, 132, 132, 131, 128, 451, 166, 363, - /* 300 */ 298, 134, 134, 134, 134, 133, 133, 132, 132, 132, - /* 310 */ 131, 128, 451, 1293, 461, 1570, 423, 377, 275, 134, + /* 250 */ 131, 128, 451, 136, 136, 136, 136, 538, 483, 137, + /* 260 */ 138, 91, 1019, 1228, 1228, 1063, 1066, 1053, 1053, 135, + /* 270 */ 135, 136, 136, 136, 136, 1085, 576, 1204, 132, 132, + /* 280 */ 132, 131, 128, 451, 93, 214, 134, 134, 134, 134, + /* 290 */ 133, 133, 132, 132, 132, 131, 128, 451, 401, 19, + /* 300 */ 19, 134, 134, 134, 134, 133, 133, 132, 132, 132, + /* 310 */ 131, 128, 451, 1498, 426, 267, 344, 467, 332, 134, /* 320 */ 134, 134, 134, 133, 133, 132, 132, 132, 131, 128, - /* 330 */ 451, 418, 320, 567, 1292, 1204, 1205, 1204, 257, 413, - /* 340 */ 483, 511, 508, 507, 94, 132, 132, 132, 131, 128, - /* 350 */ 451, 506, 1204, 548, 548, 388, 576, 384, 7, 413, - /* 360 */ 550, 229, 522, 137, 138, 91, 530, 1228, 1228, 1063, - /* 370 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 51, - /* 380 */ 51, 1582, 380, 137, 138, 91, 331, 1228, 1228, 1063, - /* 390 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 320, - /* 400 */ 567, 288, 288, 320, 567, 1602, 582, 2, 1259, 1204, - /* 410 */ 1205, 1204, 1628, 321, 573, 155, 562, 576, 1511, 264, - /* 420 */ 231, 520, 1341, 134, 134, 134, 134, 133, 133, 132, - /* 430 */ 132, 132, 131, 128, 451, 519, 1511, 1513, 1333, 1333, - /* 440 */ 82, 82, 498, 134, 134, 134, 134, 133, 133, 132, - /* 450 */ 132, 132, 131, 128, 451, 1435, 257, 288, 288, 511, - /* 460 */ 508, 507, 944, 1568, 413, 1019, 1204, 943, 360, 506, - /* 470 */ 573, 1598, 562, 44, 575, 551, 551, 557, 1107, 1582, - /* 480 */ 544, 576, 1107, 40, 417, 245, 531, 1505, 137, 138, + /* 330 */ 451, 1281, 576, 6, 1204, 1205, 1204, 257, 576, 413, + /* 340 */ 511, 508, 507, 1279, 94, 1019, 464, 1204, 551, 551, + /* 350 */ 506, 1224, 1571, 44, 38, 51, 51, 411, 576, 413, + /* 360 */ 45, 51, 51, 137, 138, 91, 530, 1228, 1228, 1063, + /* 370 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 398, + /* 380 */ 1148, 82, 82, 137, 138, 91, 39, 1228, 1228, 1063, + /* 390 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 344, + /* 400 */ 44, 288, 288, 375, 1204, 1205, 1204, 209, 1204, 1224, + /* 410 */ 320, 567, 471, 576, 573, 576, 562, 576, 316, 264, + /* 420 */ 231, 46, 160, 134, 134, 134, 134, 133, 133, 132, + /* 430 */ 132, 132, 131, 128, 451, 303, 82, 82, 82, 82, + /* 440 */ 82, 82, 442, 134, 134, 134, 134, 133, 133, 132, + /* 450 */ 132, 132, 131, 128, 451, 1582, 544, 320, 567, 1250, + /* 460 */ 874, 1582, 380, 382, 413, 1204, 1205, 1204, 360, 182, + /* 470 */ 288, 288, 1576, 557, 1339, 557, 7, 557, 1277, 472, + /* 480 */ 346, 526, 531, 573, 556, 562, 439, 1511, 137, 138, /* 490 */ 91, 219, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, - /* 500 */ 136, 136, 136, 136, 81, 81, 1281, 1204, 413, 553, - /* 510 */ 1511, 48, 512, 448, 447, 493, 578, 455, 578, 344, - /* 520 */ 45, 1204, 1233, 1204, 1205, 1204, 428, 1235, 158, 882, - /* 530 */ 320, 567, 137, 138, 91, 1234, 1228, 1228, 1063, 1066, + /* 500 */ 136, 136, 136, 136, 465, 1511, 1513, 532, 413, 288, + /* 510 */ 288, 423, 512, 288, 288, 411, 288, 288, 874, 130, + /* 520 */ 127, 234, 573, 1107, 562, 1204, 573, 1107, 562, 573, + /* 530 */ 560, 562, 137, 138, 91, 1293, 1228, 1228, 1063, 1066, /* 540 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 134, 134, /* 550 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, - /* 560 */ 1236, 576, 1236, 329, 1204, 1205, 1204, 387, 492, 403, - /* 570 */ 1040, 382, 489, 123, 568, 1569, 4, 377, 1204, 1205, - /* 580 */ 1204, 570, 570, 570, 82, 82, 882, 1029, 1331, 1331, - /* 590 */ 571, 1028, 134, 134, 134, 134, 133, 133, 132, 132, - /* 600 */ 132, 131, 128, 451, 288, 288, 1281, 1204, 576, 423, - /* 610 */ 576, 1568, 413, 423, 452, 378, 886, 573, 1279, 562, - /* 620 */ 46, 557, 532, 1028, 1028, 1030, 565, 130, 127, 234, - /* 630 */ 556, 82, 82, 82, 82, 479, 137, 138, 91, 462, + /* 560 */ 493, 503, 1292, 1204, 257, 288, 288, 511, 508, 507, + /* 570 */ 1204, 1628, 1169, 123, 568, 275, 4, 506, 573, 1511, + /* 580 */ 562, 331, 1204, 1205, 1204, 1169, 548, 548, 1169, 261, + /* 590 */ 571, 7, 134, 134, 134, 134, 133, 133, 132, 132, + /* 600 */ 132, 131, 128, 451, 108, 533, 130, 127, 234, 1204, + /* 610 */ 448, 447, 413, 1451, 452, 983, 886, 96, 1598, 1233, + /* 620 */ 1204, 1205, 1204, 984, 1235, 1450, 565, 1204, 1205, 1204, + /* 630 */ 229, 522, 1234, 534, 1333, 1333, 137, 138, 91, 1449, /* 640 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, - /* 650 */ 136, 136, 1188, 487, 1506, 1040, 413, 6, 1204, 50, - /* 660 */ 879, 121, 121, 948, 1204, 1205, 1204, 358, 557, 122, - /* 670 */ 316, 452, 577, 452, 535, 1204, 1028, 439, 303, 212, - /* 680 */ 137, 138, 91, 213, 1228, 1228, 1063, 1066, 1053, 1053, + /* 650 */ 136, 136, 373, 1595, 971, 1040, 413, 1236, 418, 1236, + /* 660 */ 879, 121, 121, 948, 373, 1595, 1204, 1205, 1204, 122, + /* 670 */ 1204, 452, 577, 452, 363, 417, 1028, 882, 373, 1595, + /* 680 */ 137, 138, 91, 462, 1228, 1228, 1063, 1066, 1053, 1053, /* 690 */ 135, 135, 136, 136, 136, 136, 134, 134, 134, 134, /* 700 */ 133, 133, 132, 132, 132, 131, 128, 451, 1028, 1028, - /* 710 */ 1030, 1031, 35, 288, 288, 1204, 1205, 1204, 1040, 1339, - /* 720 */ 533, 123, 568, 1569, 4, 377, 573, 1019, 562, 353, - /* 730 */ 1277, 356, 1204, 1205, 1204, 1029, 488, 1188, 571, 1028, + /* 710 */ 1030, 1031, 35, 570, 570, 570, 197, 423, 1040, 198, + /* 720 */ 1204, 123, 568, 1204, 4, 320, 567, 1204, 1205, 1204, + /* 730 */ 40, 388, 576, 384, 882, 1029, 423, 1188, 571, 1028, /* 740 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, - /* 750 */ 128, 451, 576, 343, 288, 288, 449, 449, 449, 971, - /* 760 */ 413, 1627, 452, 911, 1187, 288, 288, 573, 464, 562, - /* 770 */ 238, 1028, 1028, 1030, 565, 82, 82, 498, 573, 411, - /* 780 */ 562, 344, 467, 332, 137, 138, 91, 197, 1228, 1228, + /* 750 */ 128, 451, 529, 1568, 1204, 19, 19, 1204, 575, 492, + /* 760 */ 413, 157, 452, 489, 1187, 1331, 1331, 5, 1204, 949, + /* 770 */ 431, 1028, 1028, 1030, 565, 22, 22, 1204, 1205, 1204, + /* 780 */ 1204, 1205, 1204, 477, 137, 138, 91, 212, 1228, 1228, /* 790 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, - /* 800 */ 1188, 528, 1169, 1040, 413, 1110, 1110, 495, 1041, 121, - /* 810 */ 121, 1204, 317, 540, 862, 1169, 1244, 122, 1169, 452, - /* 820 */ 577, 452, 1340, 198, 1028, 1204, 481, 526, 137, 138, - /* 830 */ 91, 560, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, + /* 800 */ 1188, 48, 111, 1040, 413, 1204, 213, 970, 1041, 121, + /* 810 */ 121, 1204, 1205, 1204, 1204, 1205, 1204, 122, 221, 452, + /* 820 */ 577, 452, 44, 487, 1028, 1204, 1205, 1204, 137, 138, + /* 830 */ 91, 378, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, /* 840 */ 136, 136, 136, 136, 134, 134, 134, 134, 133, 133, /* 850 */ 132, 132, 132, 131, 128, 451, 1028, 1028, 1030, 1031, - /* 860 */ 35, 1204, 288, 288, 1204, 477, 288, 288, 1204, 1205, - /* 870 */ 1204, 539, 481, 437, 470, 573, 1451, 562, 364, 573, - /* 880 */ 1153, 562, 1204, 1205, 1204, 1188, 5, 576, 134, 134, + /* 860 */ 35, 461, 1204, 1205, 1204, 1569, 1040, 377, 214, 1149, + /* 870 */ 1657, 535, 1657, 437, 902, 320, 567, 1568, 364, 320, + /* 880 */ 567, 412, 329, 1029, 519, 1188, 3, 1028, 134, 134, /* 890 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, - /* 900 */ 221, 214, 302, 96, 1149, 1657, 232, 1657, 413, 392, - /* 910 */ 19, 19, 1024, 949, 406, 373, 1595, 1085, 1204, 1205, - /* 920 */ 1204, 1204, 1205, 1204, 1204, 426, 1149, 1658, 413, 1658, - /* 930 */ 1659, 399, 137, 138, 91, 3, 1228, 1228, 1063, 1066, - /* 940 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 304, 1311, - /* 950 */ 514, 1204, 137, 138, 91, 1498, 1228, 1228, 1063, 1066, - /* 960 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 434, 131, - /* 970 */ 128, 451, 375, 1204, 274, 291, 372, 517, 367, 516, - /* 980 */ 262, 1204, 1205, 1204, 1147, 227, 363, 448, 447, 1435, - /* 990 */ 1568, 1310, 134, 134, 134, 134, 133, 133, 132, 132, - /* 1000 */ 132, 131, 128, 451, 1568, 576, 1147, 487, 1204, 1205, - /* 1010 */ 1204, 442, 134, 134, 134, 134, 133, 133, 132, 132, - /* 1020 */ 132, 131, 128, 451, 386, 576, 485, 576, 19, 19, - /* 1030 */ 1204, 1205, 1204, 1345, 1236, 970, 1236, 574, 47, 936, - /* 1040 */ 936, 473, 413, 431, 1552, 573, 1125, 562, 19, 19, - /* 1050 */ 19, 19, 49, 336, 850, 851, 852, 111, 1368, 315, - /* 1060 */ 429, 576, 413, 433, 341, 306, 137, 138, 91, 115, + /* 900 */ 1659, 399, 1169, 307, 893, 307, 515, 576, 413, 214, + /* 910 */ 498, 944, 1024, 540, 903, 1169, 943, 392, 1169, 1028, + /* 920 */ 1028, 1030, 406, 298, 1204, 50, 1149, 1658, 413, 1658, + /* 930 */ 145, 145, 137, 138, 91, 293, 1228, 1228, 1063, 1066, + /* 940 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 1188, 1147, + /* 950 */ 514, 1568, 137, 138, 91, 1505, 1228, 1228, 1063, 1066, + /* 960 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 434, 323, + /* 970 */ 435, 539, 111, 1506, 274, 291, 372, 517, 367, 516, + /* 980 */ 262, 1204, 1205, 1204, 1574, 481, 363, 576, 7, 1569, + /* 990 */ 1568, 377, 134, 134, 134, 134, 133, 133, 132, 132, + /* 1000 */ 132, 131, 128, 451, 1568, 576, 1147, 576, 232, 576, + /* 1010 */ 19, 19, 134, 134, 134, 134, 133, 133, 132, 132, + /* 1020 */ 132, 131, 128, 451, 1169, 433, 576, 1207, 19, 19, + /* 1030 */ 19, 19, 19, 19, 1627, 576, 911, 1169, 47, 120, + /* 1040 */ 1169, 117, 413, 306, 498, 438, 1125, 206, 336, 19, + /* 1050 */ 19, 1435, 49, 449, 449, 449, 1368, 315, 81, 81, + /* 1060 */ 576, 304, 413, 1570, 207, 377, 137, 138, 91, 115, /* 1070 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, - /* 1080 */ 136, 136, 576, 1309, 82, 82, 137, 138, 91, 529, + /* 1080 */ 136, 136, 576, 82, 82, 1207, 137, 138, 91, 1340, /* 1090 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, - /* 1100 */ 136, 136, 1569, 222, 377, 19, 19, 305, 1126, 1169, - /* 1110 */ 398, 1148, 22, 22, 498, 333, 1569, 335, 377, 576, - /* 1120 */ 438, 445, 1169, 1127, 486, 1169, 134, 134, 134, 134, + /* 1100 */ 136, 136, 1569, 386, 377, 82, 82, 463, 1126, 1552, + /* 1110 */ 333, 463, 335, 131, 128, 451, 1569, 161, 377, 16, + /* 1120 */ 317, 387, 428, 1127, 448, 447, 134, 134, 134, 134, /* 1130 */ 133, 133, 132, 132, 132, 131, 128, 451, 1128, 576, - /* 1140 */ 902, 576, 145, 145, 6, 576, 134, 134, 134, 134, - /* 1150 */ 133, 133, 132, 132, 132, 131, 128, 451, 214, 1336, - /* 1160 */ 922, 576, 19, 19, 19, 19, 1282, 419, 19, 19, - /* 1170 */ 923, 412, 515, 141, 576, 1169, 413, 206, 465, 207, - /* 1180 */ 903, 215, 1575, 552, 147, 147, 7, 227, 1169, 411, - /* 1190 */ 1250, 1169, 120, 307, 117, 307, 413, 66, 66, 334, + /* 1140 */ 1105, 10, 445, 267, 576, 1554, 134, 134, 134, 134, + /* 1150 */ 133, 133, 132, 132, 132, 131, 128, 451, 532, 576, + /* 1160 */ 922, 576, 19, 19, 576, 1573, 576, 147, 147, 7, + /* 1170 */ 923, 1236, 498, 1236, 576, 487, 413, 552, 285, 1224, + /* 1180 */ 969, 215, 82, 82, 66, 66, 1435, 67, 67, 21, + /* 1190 */ 21, 1110, 1110, 495, 334, 297, 413, 53, 53, 297, /* 1200 */ 137, 138, 91, 119, 1228, 1228, 1063, 1066, 1053, 1053, - /* 1210 */ 135, 135, 136, 136, 136, 136, 413, 285, 209, 969, - /* 1220 */ 137, 138, 91, 471, 1228, 1228, 1063, 1066, 1053, 1053, - /* 1230 */ 135, 135, 136, 136, 136, 136, 435, 10, 1450, 267, - /* 1240 */ 137, 126, 91, 1435, 1228, 1228, 1063, 1066, 1053, 1053, - /* 1250 */ 135, 135, 136, 136, 136, 136, 1435, 1435, 410, 409, + /* 1210 */ 135, 135, 136, 136, 136, 136, 413, 1336, 1311, 446, + /* 1220 */ 137, 138, 91, 227, 1228, 1228, 1063, 1066, 1053, 1053, + /* 1230 */ 135, 135, 136, 136, 136, 136, 574, 1224, 936, 936, + /* 1240 */ 137, 126, 91, 141, 1228, 1228, 1063, 1066, 1053, 1053, + /* 1250 */ 135, 135, 136, 136, 136, 136, 533, 429, 472, 346, /* 1260 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, - /* 1270 */ 128, 451, 576, 969, 576, 1224, 498, 373, 1595, 1554, + /* 1270 */ 128, 451, 576, 457, 233, 343, 1435, 403, 498, 1550, /* 1280 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, - /* 1290 */ 128, 451, 532, 457, 576, 82, 82, 82, 82, 111, + /* 1290 */ 128, 451, 576, 324, 576, 82, 82, 487, 576, 969, /* 1300 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, - /* 1310 */ 128, 451, 109, 233, 430, 1576, 546, 67, 67, 7, - /* 1320 */ 413, 351, 550, 1550, 260, 259, 258, 494, 443, 569, - /* 1330 */ 419, 983, 446, 1224, 450, 545, 1207, 576, 969, 984, - /* 1340 */ 413, 475, 1449, 1574, 1180, 138, 91, 7, 1228, 1228, + /* 1310 */ 128, 451, 288, 288, 546, 68, 68, 54, 54, 553, + /* 1320 */ 413, 69, 69, 351, 6, 573, 944, 562, 410, 409, + /* 1330 */ 1435, 943, 450, 545, 260, 259, 258, 576, 158, 576, + /* 1340 */ 413, 222, 1180, 479, 969, 138, 91, 430, 1228, 1228, /* 1350 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, - /* 1360 */ 21, 21, 267, 576, 300, 1126, 91, 233, 1228, 1228, + /* 1360 */ 70, 70, 71, 71, 576, 1126, 91, 576, 1228, 1228, /* 1370 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, - /* 1380 */ 1127, 373, 1595, 161, 1573, 16, 53, 53, 7, 108, - /* 1390 */ 533, 38, 969, 125, 1207, 1128, 1180, 576, 1224, 123, - /* 1400 */ 568, 893, 4, 324, 134, 134, 134, 134, 133, 133, - /* 1410 */ 132, 132, 132, 131, 128, 451, 571, 564, 534, 576, - /* 1420 */ 68, 68, 576, 39, 134, 134, 134, 134, 133, 133, - /* 1430 */ 132, 132, 132, 131, 128, 451, 576, 160, 1571, 1223, - /* 1440 */ 452, 576, 54, 54, 576, 69, 69, 576, 1366, 576, - /* 1450 */ 420, 184, 565, 463, 297, 576, 1224, 463, 297, 70, - /* 1460 */ 70, 576, 44, 474, 71, 71, 576, 72, 72, 576, - /* 1470 */ 73, 73, 55, 55, 411, 874, 242, 576, 56, 56, - /* 1480 */ 576, 1040, 576, 478, 57, 57, 576, 121, 121, 59, - /* 1490 */ 59, 23, 60, 60, 411, 122, 319, 452, 577, 452, - /* 1500 */ 74, 74, 1028, 75, 75, 76, 76, 411, 290, 20, - /* 1510 */ 20, 108, 287, 231, 553, 123, 568, 325, 4, 320, - /* 1520 */ 567, 97, 218, 944, 1144, 328, 400, 576, 943, 576, - /* 1530 */ 1380, 424, 571, 874, 1028, 1028, 1030, 1031, 35, 293, - /* 1540 */ 534, 576, 1104, 576, 1104, 9, 576, 342, 576, 111, - /* 1550 */ 77, 77, 143, 143, 576, 205, 452, 222, 1379, 889, - /* 1560 */ 576, 901, 900, 1188, 144, 144, 78, 78, 565, 62, - /* 1570 */ 62, 79, 79, 323, 1021, 576, 266, 63, 63, 908, - /* 1580 */ 909, 1589, 542, 80, 80, 576, 371, 541, 123, 568, - /* 1590 */ 480, 4, 266, 482, 244, 266, 370, 1040, 64, 64, - /* 1600 */ 576, 466, 576, 121, 121, 571, 1557, 576, 170, 170, - /* 1610 */ 576, 122, 576, 452, 577, 452, 576, 889, 1028, 576, - /* 1620 */ 165, 576, 111, 171, 171, 87, 87, 337, 1616, 452, - /* 1630 */ 65, 65, 1530, 83, 83, 146, 146, 986, 987, 84, - /* 1640 */ 84, 565, 168, 168, 148, 148, 1092, 347, 1032, 111, - /* 1650 */ 1028, 1028, 1030, 1031, 35, 542, 1103, 576, 1103, 576, - /* 1660 */ 543, 123, 568, 504, 4, 263, 576, 361, 1529, 111, - /* 1670 */ 1040, 1088, 576, 263, 576, 490, 121, 121, 571, 1188, - /* 1680 */ 142, 142, 169, 169, 122, 576, 452, 577, 452, 162, - /* 1690 */ 162, 1028, 576, 563, 576, 152, 152, 151, 151, 348, - /* 1700 */ 1376, 974, 452, 266, 1092, 942, 1032, 125, 149, 149, - /* 1710 */ 939, 576, 125, 576, 565, 150, 150, 86, 86, 872, - /* 1720 */ 352, 159, 576, 1028, 1028, 1030, 1031, 35, 542, 941, - /* 1730 */ 576, 125, 355, 541, 88, 88, 85, 85, 357, 359, - /* 1740 */ 1324, 1308, 366, 1040, 376, 52, 52, 499, 1389, 121, - /* 1750 */ 121, 1434, 1188, 58, 58, 1362, 1374, 122, 1439, 452, - /* 1760 */ 577, 452, 1289, 167, 1028, 1280, 280, 1268, 1267, 1269, - /* 1770 */ 1609, 1359, 312, 313, 12, 314, 397, 1421, 224, 1416, - /* 1780 */ 295, 237, 1409, 339, 340, 1426, 301, 345, 484, 228, - /* 1790 */ 1371, 1307, 1372, 1370, 1425, 404, 1028, 1028, 1030, 1031, - /* 1800 */ 35, 1601, 1192, 454, 509, 369, 292, 1502, 210, 1501, - /* 1810 */ 1369, 396, 396, 395, 277, 393, 211, 566, 859, 1612, - /* 1820 */ 1244, 123, 568, 391, 4, 1188, 223, 270, 1549, 1547, - /* 1830 */ 1241, 239, 186, 327, 422, 96, 195, 220, 571, 235, - /* 1840 */ 180, 326, 188, 468, 190, 1507, 191, 192, 92, 193, - /* 1850 */ 469, 95, 1422, 13, 502, 247, 1430, 109, 199, 402, - /* 1860 */ 476, 405, 452, 1496, 1428, 1427, 14, 491, 251, 102, - /* 1870 */ 497, 1518, 241, 281, 565, 253, 203, 354, 500, 254, - /* 1880 */ 175, 1270, 407, 43, 350, 518, 1327, 436, 255, 1326, - /* 1890 */ 1325, 1318, 104, 893, 1626, 229, 408, 440, 1625, 441, - /* 1900 */ 240, 310, 1296, 1040, 311, 1317, 527, 1594, 1297, 121, - /* 1910 */ 121, 368, 1295, 1624, 268, 269, 1580, 122, 1579, 452, - /* 1920 */ 577, 452, 374, 444, 1028, 1394, 1393, 140, 553, 90, - /* 1930 */ 568, 11, 4, 1483, 383, 414, 385, 110, 116, 216, - /* 1940 */ 320, 567, 1350, 555, 42, 318, 571, 537, 1349, 389, - /* 1950 */ 390, 579, 1198, 276, 279, 278, 1028, 1028, 1030, 1031, - /* 1960 */ 35, 580, 415, 1265, 458, 1260, 416, 185, 1534, 172, - /* 1970 */ 452, 1535, 173, 156, 308, 846, 1533, 1532, 453, 217, - /* 1980 */ 225, 89, 565, 174, 322, 1188, 226, 236, 1102, 154, - /* 1990 */ 1100, 330, 176, 187, 1223, 189, 925, 338, 243, 1116, - /* 2000 */ 246, 194, 177, 178, 425, 427, 98, 99, 196, 100, - /* 2010 */ 101, 1040, 179, 1119, 248, 1115, 249, 121, 121, 24, - /* 2020 */ 163, 250, 349, 1108, 266, 122, 1238, 452, 577, 452, - /* 2030 */ 1192, 454, 1028, 200, 292, 496, 252, 201, 861, 396, + /* 1380 */ 1127, 166, 850, 851, 852, 1282, 419, 72, 72, 108, + /* 1390 */ 73, 73, 1310, 358, 1180, 1128, 576, 305, 576, 123, + /* 1400 */ 568, 494, 4, 488, 134, 134, 134, 134, 133, 133, + /* 1410 */ 132, 132, 132, 131, 128, 451, 571, 564, 534, 55, + /* 1420 */ 55, 56, 56, 576, 134, 134, 134, 134, 133, 133, + /* 1430 */ 132, 132, 132, 131, 128, 451, 576, 1104, 233, 1104, + /* 1440 */ 452, 1602, 582, 2, 1259, 576, 57, 57, 576, 321, + /* 1450 */ 576, 155, 565, 1435, 485, 353, 576, 356, 1341, 59, + /* 1460 */ 59, 576, 44, 969, 569, 419, 576, 238, 60, 60, + /* 1470 */ 261, 74, 74, 75, 75, 287, 231, 576, 1366, 76, + /* 1480 */ 76, 1040, 420, 184, 20, 20, 576, 121, 121, 77, + /* 1490 */ 77, 97, 218, 288, 288, 122, 125, 452, 577, 452, + /* 1500 */ 143, 143, 1028, 576, 520, 576, 573, 576, 562, 144, + /* 1510 */ 144, 474, 227, 1244, 478, 123, 568, 576, 4, 320, + /* 1520 */ 567, 245, 411, 576, 443, 411, 78, 78, 62, 62, + /* 1530 */ 79, 79, 571, 319, 1028, 1028, 1030, 1031, 35, 418, + /* 1540 */ 63, 63, 576, 290, 411, 9, 80, 80, 1144, 576, + /* 1550 */ 400, 576, 486, 455, 576, 1223, 452, 576, 325, 342, + /* 1560 */ 576, 111, 576, 1188, 242, 64, 64, 473, 565, 576, + /* 1570 */ 23, 576, 170, 170, 171, 171, 576, 87, 87, 328, + /* 1580 */ 65, 65, 542, 83, 83, 146, 146, 541, 123, 568, + /* 1590 */ 341, 4, 84, 84, 168, 168, 576, 1040, 576, 148, + /* 1600 */ 148, 576, 1380, 121, 121, 571, 1021, 576, 266, 576, + /* 1610 */ 424, 122, 576, 452, 577, 452, 576, 553, 1028, 142, + /* 1620 */ 142, 169, 169, 576, 162, 162, 528, 889, 371, 452, + /* 1630 */ 152, 152, 151, 151, 1379, 149, 149, 109, 370, 150, + /* 1640 */ 150, 565, 576, 480, 576, 266, 86, 86, 576, 1092, + /* 1650 */ 1028, 1028, 1030, 1031, 35, 542, 482, 576, 266, 466, + /* 1660 */ 543, 123, 568, 1616, 4, 88, 88, 85, 85, 475, + /* 1670 */ 1040, 52, 52, 222, 901, 900, 121, 121, 571, 1188, + /* 1680 */ 58, 58, 244, 1032, 122, 889, 452, 577, 452, 908, + /* 1690 */ 909, 1028, 300, 347, 504, 111, 263, 361, 165, 111, + /* 1700 */ 111, 1088, 452, 263, 974, 1153, 266, 1092, 986, 987, + /* 1710 */ 942, 939, 125, 125, 565, 1103, 872, 1103, 159, 941, + /* 1720 */ 1309, 125, 1557, 1028, 1028, 1030, 1031, 35, 542, 337, + /* 1730 */ 1530, 205, 1529, 541, 499, 1589, 490, 348, 1376, 352, + /* 1740 */ 355, 1032, 357, 1040, 359, 1324, 1308, 366, 563, 121, + /* 1750 */ 121, 376, 1188, 1389, 1434, 1362, 280, 122, 1374, 452, + /* 1760 */ 577, 452, 167, 1439, 1028, 1289, 1280, 1268, 1267, 1269, + /* 1770 */ 1609, 1359, 312, 313, 314, 397, 12, 237, 224, 1421, + /* 1780 */ 295, 1416, 1409, 1426, 339, 484, 340, 509, 1371, 1612, + /* 1790 */ 1372, 1425, 1244, 404, 301, 228, 1028, 1028, 1030, 1031, + /* 1800 */ 35, 1601, 1192, 454, 345, 1307, 292, 369, 1502, 1501, + /* 1810 */ 270, 396, 396, 395, 277, 393, 1370, 1369, 859, 1549, + /* 1820 */ 186, 123, 568, 235, 4, 1188, 391, 210, 211, 223, + /* 1830 */ 1547, 239, 1241, 327, 422, 96, 220, 195, 571, 180, + /* 1840 */ 188, 326, 468, 469, 190, 191, 502, 192, 193, 566, + /* 1850 */ 247, 109, 1430, 491, 199, 251, 102, 281, 402, 476, + /* 1860 */ 405, 1496, 452, 497, 253, 1422, 13, 1428, 14, 1427, + /* 1870 */ 203, 1507, 241, 500, 565, 354, 407, 92, 95, 1270, + /* 1880 */ 175, 254, 518, 43, 1327, 255, 1326, 1325, 436, 1518, + /* 1890 */ 350, 1318, 104, 229, 893, 1626, 440, 441, 1625, 408, + /* 1900 */ 240, 1296, 268, 1040, 310, 269, 1297, 527, 444, 121, + /* 1910 */ 121, 368, 1295, 1594, 1624, 311, 1394, 122, 1317, 452, + /* 1920 */ 577, 452, 374, 1580, 1028, 1393, 140, 553, 11, 90, + /* 1930 */ 568, 385, 4, 116, 318, 414, 1579, 110, 1483, 537, + /* 1940 */ 320, 567, 1350, 555, 42, 579, 571, 1349, 1198, 383, + /* 1950 */ 276, 390, 216, 389, 278, 279, 1028, 1028, 1030, 1031, + /* 1960 */ 35, 172, 580, 1265, 458, 1260, 415, 416, 185, 156, + /* 1970 */ 452, 1534, 1535, 173, 1533, 1532, 89, 308, 225, 226, + /* 1980 */ 846, 174, 565, 453, 217, 1188, 322, 236, 1102, 154, + /* 1990 */ 1100, 330, 187, 176, 1223, 243, 189, 925, 338, 246, + /* 2000 */ 1116, 194, 177, 425, 178, 427, 98, 196, 99, 100, + /* 2010 */ 101, 1040, 179, 1119, 1115, 248, 249, 121, 121, 163, + /* 2020 */ 24, 250, 349, 1238, 496, 122, 1108, 452, 577, 452, + /* 2030 */ 1192, 454, 1028, 266, 292, 200, 252, 201, 861, 396, /* 2040 */ 396, 395, 277, 393, 15, 501, 859, 370, 292, 256, /* 2050 */ 202, 554, 505, 396, 396, 395, 277, 393, 103, 239, /* 2060 */ 859, 327, 25, 26, 1028, 1028, 1030, 1031, 35, 326, /* 2070 */ 362, 510, 891, 239, 365, 327, 513, 904, 105, 309, /* 2080 */ 164, 181, 27, 326, 106, 521, 107, 1185, 1069, 1155, - /* 2090 */ 17, 1154, 284, 1188, 286, 978, 265, 204, 125, 1171, - /* 2100 */ 241, 230, 972, 1175, 28, 1160, 29, 1179, 175, 1173, - /* 2110 */ 30, 43, 31, 1178, 241, 32, 41, 549, 8, 33, - /* 2120 */ 208, 111, 175, 1083, 1070, 43, 113, 1068, 240, 114, - /* 2130 */ 1072, 34, 1073, 561, 1124, 118, 271, 36, 18, 1194, - /* 2140 */ 1033, 873, 240, 935, 124, 37, 272, 273, 1617, 572, - /* 2150 */ 183, 153, 394, 1193, 1256, 1256, 1256, 1256, 1256, 1256, + /* 2090 */ 17, 1154, 230, 1188, 284, 286, 265, 204, 125, 1171, + /* 2100 */ 241, 28, 978, 972, 29, 41, 1175, 1179, 175, 1173, + /* 2110 */ 30, 43, 31, 8, 241, 1178, 32, 1160, 208, 549, + /* 2120 */ 33, 111, 175, 1083, 1070, 43, 1068, 1072, 240, 113, + /* 2130 */ 114, 34, 561, 118, 1124, 271, 1073, 36, 18, 572, + /* 2140 */ 1033, 873, 240, 124, 37, 935, 272, 273, 1617, 183, + /* 2150 */ 153, 394, 1194, 1193, 1256, 1256, 1256, 1256, 1256, 1256, /* 2160 */ 1256, 1256, 1256, 414, 1256, 1256, 1256, 1256, 320, 567, /* 2170 */ 1256, 1256, 1256, 1256, 1256, 1256, 1256, 414, 1256, 1256, /* 2180 */ 1256, 1256, 320, 567, 1256, 1256, 1256, 1256, 1256, 1256, @@ -174761,257 +175051,257 @@ static const YYACTIONTYPE yy_action[] = { /* 2200 */ 1256, 1256, 1256, 1256, 1256, 1256, 458, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 276, 277, 278, 240, 241, 224, 194, 226, 194, 240, - /* 10 */ 241, 194, 216, 220, 194, 234, 253, 194, 255, 19, - /* 20 */ 224, 297, 253, 194, 255, 205, 212, 213, 205, 217, - /* 30 */ 218, 31, 205, 194, 217, 218, 194, 217, 218, 39, - /* 40 */ 217, 218, 312, 43, 44, 45, 316, 47, 48, 49, + /* 0 */ 277, 278, 279, 241, 242, 225, 195, 227, 195, 241, + /* 10 */ 242, 195, 217, 221, 195, 235, 254, 195, 256, 19, + /* 20 */ 225, 298, 254, 195, 256, 206, 213, 214, 206, 218, + /* 30 */ 219, 31, 206, 195, 218, 219, 195, 218, 219, 39, + /* 40 */ 218, 219, 313, 43, 44, 45, 317, 47, 48, 49, /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 19, - /* 60 */ 240, 241, 194, 240, 241, 194, 254, 240, 241, 276, - /* 70 */ 277, 278, 233, 253, 254, 255, 253, 254, 255, 217, - /* 80 */ 253, 239, 255, 43, 44, 45, 263, 47, 48, 49, - /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 270, - /* 100 */ 286, 22, 23, 103, 104, 105, 106, 107, 108, 109, - /* 110 */ 110, 111, 112, 113, 114, 82, 47, 48, 49, 50, - /* 120 */ 186, 187, 188, 189, 190, 191, 189, 87, 191, 89, - /* 130 */ 196, 19, 198, 196, 317, 198, 319, 25, 194, 205, - /* 140 */ 298, 270, 205, 103, 104, 105, 106, 107, 108, 109, - /* 150 */ 110, 111, 112, 113, 114, 43, 44, 45, 11, 47, + /* 60 */ 241, 242, 195, 241, 242, 195, 255, 241, 242, 277, + /* 70 */ 278, 279, 234, 254, 255, 256, 254, 255, 256, 218, + /* 80 */ 254, 240, 256, 43, 44, 45, 264, 47, 48, 49, + /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 271, + /* 100 */ 287, 22, 23, 103, 104, 105, 106, 107, 108, 109, + /* 110 */ 110, 111, 112, 113, 114, 114, 47, 48, 49, 50, + /* 120 */ 187, 188, 189, 190, 191, 192, 190, 87, 192, 89, + /* 130 */ 197, 19, 199, 197, 318, 199, 320, 25, 195, 206, + /* 140 */ 299, 271, 206, 103, 104, 105, 106, 107, 108, 109, + /* 150 */ 110, 111, 112, 113, 114, 43, 44, 45, 195, 47, /* 160 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 170 */ 58, 60, 139, 140, 240, 241, 214, 240, 241, 311, - /* 180 */ 312, 102, 70, 239, 316, 194, 19, 253, 77, 255, - /* 190 */ 253, 122, 255, 55, 56, 57, 58, 59, 207, 88, - /* 200 */ 194, 90, 268, 194, 93, 268, 107, 108, 109, 110, - /* 210 */ 111, 112, 113, 114, 47, 103, 104, 105, 106, 107, - /* 220 */ 108, 109, 110, 111, 112, 113, 114, 114, 117, 118, - /* 230 */ 119, 276, 277, 278, 300, 19, 194, 300, 276, 277, - /* 240 */ 278, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 250 */ 112, 113, 114, 55, 56, 57, 58, 146, 194, 43, - /* 260 */ 44, 45, 47, 47, 48, 49, 50, 51, 52, 53, - /* 270 */ 54, 55, 56, 57, 58, 82, 129, 130, 60, 129, - /* 280 */ 130, 217, 218, 116, 68, 25, 103, 104, 105, 106, - /* 290 */ 107, 108, 109, 110, 111, 112, 113, 114, 23, 132, - /* 300 */ 294, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 310 */ 112, 113, 114, 217, 121, 306, 194, 308, 26, 103, + /* 170 */ 58, 60, 21, 195, 241, 242, 215, 241, 242, 312, + /* 180 */ 313, 102, 70, 205, 317, 207, 242, 254, 77, 256, + /* 190 */ 254, 122, 256, 55, 56, 57, 58, 59, 254, 88, + /* 200 */ 256, 90, 269, 240, 93, 269, 107, 108, 109, 110, + /* 210 */ 111, 112, 113, 114, 271, 103, 104, 105, 106, 107, + /* 220 */ 108, 109, 110, 111, 112, 113, 114, 313, 117, 118, + /* 230 */ 119, 317, 81, 195, 301, 19, 195, 301, 277, 278, + /* 240 */ 279, 103, 104, 105, 106, 107, 108, 109, 110, 111, + /* 250 */ 112, 113, 114, 55, 56, 57, 58, 146, 195, 43, + /* 260 */ 44, 45, 74, 47, 48, 49, 50, 51, 52, 53, + /* 270 */ 54, 55, 56, 57, 58, 124, 195, 60, 109, 110, + /* 280 */ 111, 112, 113, 114, 68, 195, 103, 104, 105, 106, + /* 290 */ 107, 108, 109, 110, 111, 112, 113, 114, 208, 218, + /* 300 */ 219, 103, 104, 105, 106, 107, 108, 109, 110, 111, + /* 310 */ 112, 113, 114, 162, 233, 24, 128, 129, 130, 103, /* 320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - /* 330 */ 114, 116, 139, 140, 217, 117, 118, 119, 120, 19, - /* 340 */ 194, 123, 124, 125, 24, 109, 110, 111, 112, 113, - /* 350 */ 114, 133, 60, 311, 312, 250, 194, 252, 316, 19, - /* 360 */ 194, 166, 167, 43, 44, 45, 205, 47, 48, 49, - /* 370 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 217, - /* 380 */ 218, 317, 318, 43, 44, 45, 264, 47, 48, 49, - /* 390 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 139, - /* 400 */ 140, 240, 241, 139, 140, 188, 189, 190, 191, 117, - /* 410 */ 118, 119, 231, 196, 253, 198, 255, 194, 194, 258, - /* 420 */ 259, 146, 205, 103, 104, 105, 106, 107, 108, 109, - /* 430 */ 110, 111, 112, 113, 114, 109, 212, 213, 236, 237, - /* 440 */ 217, 218, 194, 103, 104, 105, 106, 107, 108, 109, - /* 450 */ 110, 111, 112, 113, 114, 194, 120, 240, 241, 123, - /* 460 */ 124, 125, 136, 194, 19, 74, 60, 141, 23, 133, - /* 470 */ 253, 194, 255, 82, 194, 309, 310, 254, 29, 317, - /* 480 */ 318, 194, 33, 22, 199, 268, 263, 239, 43, 44, + /* 330 */ 114, 195, 195, 215, 117, 118, 119, 120, 195, 19, + /* 340 */ 123, 124, 125, 207, 24, 74, 246, 60, 310, 311, + /* 350 */ 133, 60, 311, 82, 22, 218, 219, 257, 195, 19, + /* 360 */ 73, 218, 219, 43, 44, 45, 206, 47, 48, 49, + /* 370 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 22, + /* 380 */ 23, 218, 219, 43, 44, 45, 54, 47, 48, 49, + /* 390 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 128, + /* 400 */ 82, 241, 242, 195, 117, 118, 119, 289, 60, 118, + /* 410 */ 139, 140, 294, 195, 254, 195, 256, 195, 255, 259, + /* 420 */ 260, 73, 22, 103, 104, 105, 106, 107, 108, 109, + /* 430 */ 110, 111, 112, 113, 114, 206, 218, 219, 218, 219, + /* 440 */ 218, 219, 234, 103, 104, 105, 106, 107, 108, 109, + /* 450 */ 110, 111, 112, 113, 114, 318, 319, 139, 140, 102, + /* 460 */ 60, 318, 319, 221, 19, 117, 118, 119, 23, 195, + /* 470 */ 241, 242, 313, 255, 206, 255, 317, 255, 206, 129, + /* 480 */ 130, 206, 264, 254, 264, 256, 264, 195, 43, 44, /* 490 */ 45, 151, 47, 48, 49, 50, 51, 52, 53, 54, - /* 500 */ 55, 56, 57, 58, 217, 218, 194, 60, 19, 146, - /* 510 */ 286, 242, 23, 107, 108, 66, 204, 300, 206, 128, - /* 520 */ 73, 60, 116, 117, 118, 119, 265, 121, 165, 60, - /* 530 */ 139, 140, 43, 44, 45, 129, 47, 48, 49, 50, + /* 500 */ 55, 56, 57, 58, 246, 213, 214, 19, 19, 241, + /* 510 */ 242, 195, 23, 241, 242, 257, 241, 242, 118, 277, + /* 520 */ 278, 279, 254, 29, 256, 60, 254, 33, 256, 254, + /* 530 */ 206, 256, 43, 44, 45, 218, 47, 48, 49, 50, /* 540 */ 51, 52, 53, 54, 55, 56, 57, 58, 103, 104, /* 550 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, - /* 560 */ 154, 194, 156, 194, 117, 118, 119, 280, 283, 205, - /* 570 */ 101, 220, 287, 19, 20, 306, 22, 308, 117, 118, - /* 580 */ 119, 211, 212, 213, 217, 218, 117, 118, 236, 237, - /* 590 */ 36, 122, 103, 104, 105, 106, 107, 108, 109, 110, - /* 600 */ 111, 112, 113, 114, 240, 241, 194, 60, 194, 194, - /* 610 */ 194, 194, 19, 194, 60, 194, 23, 253, 206, 255, - /* 620 */ 73, 254, 19, 154, 155, 156, 72, 276, 277, 278, - /* 630 */ 263, 217, 218, 217, 218, 271, 43, 44, 45, 271, + /* 560 */ 66, 19, 218, 60, 120, 241, 242, 123, 124, 125, + /* 570 */ 60, 232, 77, 19, 20, 26, 22, 133, 254, 287, + /* 580 */ 256, 265, 117, 118, 119, 90, 312, 313, 93, 47, + /* 590 */ 36, 317, 103, 104, 105, 106, 107, 108, 109, 110, + /* 600 */ 111, 112, 113, 114, 116, 117, 277, 278, 279, 60, + /* 610 */ 107, 108, 19, 276, 60, 31, 23, 152, 195, 116, + /* 620 */ 117, 118, 119, 39, 121, 276, 72, 117, 118, 119, + /* 630 */ 166, 167, 129, 145, 237, 238, 43, 44, 45, 276, /* 640 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 650 */ 57, 58, 183, 194, 285, 101, 19, 214, 60, 242, - /* 660 */ 23, 107, 108, 109, 117, 118, 119, 16, 254, 115, - /* 670 */ 254, 117, 118, 119, 194, 60, 122, 263, 205, 264, - /* 680 */ 43, 44, 45, 264, 47, 48, 49, 50, 51, 52, + /* 650 */ 57, 58, 315, 316, 144, 101, 19, 154, 116, 156, + /* 660 */ 23, 107, 108, 109, 315, 316, 117, 118, 119, 115, + /* 670 */ 60, 117, 118, 119, 132, 200, 122, 60, 315, 316, + /* 680 */ 43, 44, 45, 272, 47, 48, 49, 50, 51, 52, /* 690 */ 53, 54, 55, 56, 57, 58, 103, 104, 105, 106, /* 700 */ 107, 108, 109, 110, 111, 112, 113, 114, 154, 155, - /* 710 */ 156, 157, 158, 240, 241, 117, 118, 119, 101, 205, - /* 720 */ 117, 19, 20, 306, 22, 308, 253, 74, 255, 78, - /* 730 */ 205, 80, 117, 118, 119, 118, 293, 183, 36, 122, + /* 710 */ 156, 157, 158, 212, 213, 214, 22, 195, 101, 22, + /* 720 */ 60, 19, 20, 60, 22, 139, 140, 117, 118, 119, + /* 730 */ 22, 251, 195, 253, 117, 118, 195, 183, 36, 122, /* 740 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 750 */ 113, 114, 194, 294, 240, 241, 211, 212, 213, 144, - /* 760 */ 19, 23, 60, 25, 23, 240, 241, 253, 245, 255, - /* 770 */ 15, 154, 155, 156, 72, 217, 218, 194, 253, 256, - /* 780 */ 255, 128, 129, 130, 43, 44, 45, 22, 47, 48, + /* 750 */ 113, 114, 195, 195, 60, 218, 219, 60, 195, 284, + /* 760 */ 19, 25, 60, 288, 23, 237, 238, 22, 60, 109, + /* 770 */ 233, 154, 155, 156, 72, 218, 219, 117, 118, 119, + /* 780 */ 117, 118, 119, 116, 43, 44, 45, 265, 47, 48, /* 790 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, - /* 800 */ 183, 19, 77, 101, 19, 128, 129, 130, 23, 107, - /* 810 */ 108, 60, 254, 88, 21, 90, 61, 115, 93, 117, - /* 820 */ 118, 119, 239, 22, 122, 60, 194, 205, 43, 44, - /* 830 */ 45, 205, 47, 48, 49, 50, 51, 52, 53, 54, + /* 800 */ 183, 243, 25, 101, 19, 60, 265, 144, 23, 107, + /* 810 */ 108, 117, 118, 119, 117, 118, 119, 115, 151, 117, + /* 820 */ 118, 119, 82, 195, 122, 117, 118, 119, 43, 44, + /* 830 */ 45, 195, 47, 48, 49, 50, 51, 52, 53, 54, /* 840 */ 55, 56, 57, 58, 103, 104, 105, 106, 107, 108, /* 850 */ 109, 110, 111, 112, 113, 114, 154, 155, 156, 157, - /* 860 */ 158, 60, 240, 241, 60, 116, 240, 241, 117, 118, - /* 870 */ 119, 146, 194, 19, 81, 253, 275, 255, 24, 253, - /* 880 */ 98, 255, 117, 118, 119, 183, 22, 194, 103, 104, + /* 860 */ 158, 121, 117, 118, 119, 307, 101, 309, 195, 22, + /* 870 */ 23, 195, 25, 19, 35, 139, 140, 195, 24, 139, + /* 880 */ 140, 208, 195, 118, 109, 183, 22, 122, 103, 104, /* 890 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, - /* 900 */ 151, 194, 270, 152, 22, 23, 194, 25, 19, 202, - /* 910 */ 217, 218, 23, 109, 207, 314, 315, 124, 117, 118, - /* 920 */ 119, 117, 118, 119, 60, 232, 22, 23, 19, 25, - /* 930 */ 303, 304, 43, 44, 45, 22, 47, 48, 49, 50, - /* 940 */ 51, 52, 53, 54, 55, 56, 57, 58, 270, 227, - /* 950 */ 96, 60, 43, 44, 45, 162, 47, 48, 49, 50, - /* 960 */ 51, 52, 53, 54, 55, 56, 57, 58, 114, 112, - /* 970 */ 113, 114, 194, 60, 120, 121, 122, 123, 124, 125, - /* 980 */ 126, 117, 118, 119, 102, 25, 132, 107, 108, 194, - /* 990 */ 194, 227, 103, 104, 105, 106, 107, 108, 109, 110, - /* 1000 */ 111, 112, 113, 114, 194, 194, 102, 194, 117, 118, - /* 1010 */ 119, 233, 103, 104, 105, 106, 107, 108, 109, 110, - /* 1020 */ 111, 112, 113, 114, 194, 194, 19, 194, 217, 218, - /* 1030 */ 117, 118, 119, 241, 154, 144, 156, 135, 242, 137, - /* 1040 */ 138, 130, 19, 232, 194, 253, 23, 255, 217, 218, - /* 1050 */ 217, 218, 242, 16, 7, 8, 9, 25, 261, 262, - /* 1060 */ 265, 194, 19, 232, 153, 232, 43, 44, 45, 160, + /* 900 */ 304, 305, 77, 230, 127, 232, 67, 195, 19, 195, + /* 910 */ 195, 136, 23, 88, 75, 90, 141, 203, 93, 154, + /* 920 */ 155, 156, 208, 295, 60, 243, 22, 23, 19, 25, + /* 930 */ 218, 219, 43, 44, 45, 100, 47, 48, 49, 50, + /* 940 */ 51, 52, 53, 54, 55, 56, 57, 58, 183, 102, + /* 950 */ 96, 195, 43, 44, 45, 240, 47, 48, 49, 50, + /* 960 */ 51, 52, 53, 54, 55, 56, 57, 58, 114, 134, + /* 970 */ 131, 146, 25, 286, 120, 121, 122, 123, 124, 125, + /* 980 */ 126, 117, 118, 119, 313, 195, 132, 195, 317, 307, + /* 990 */ 195, 309, 103, 104, 105, 106, 107, 108, 109, 110, + /* 1000 */ 111, 112, 113, 114, 195, 195, 102, 195, 195, 195, + /* 1010 */ 218, 219, 103, 104, 105, 106, 107, 108, 109, 110, + /* 1020 */ 111, 112, 113, 114, 77, 233, 195, 60, 218, 219, + /* 1030 */ 218, 219, 218, 219, 23, 195, 25, 90, 243, 159, + /* 1040 */ 93, 161, 19, 233, 195, 233, 23, 233, 16, 218, + /* 1050 */ 219, 195, 243, 212, 213, 214, 262, 263, 218, 219, + /* 1060 */ 195, 271, 19, 307, 233, 309, 43, 44, 45, 160, /* 1070 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1080 */ 57, 58, 194, 227, 217, 218, 43, 44, 45, 194, + /* 1080 */ 57, 58, 195, 218, 219, 118, 43, 44, 45, 240, /* 1090 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1100 */ 57, 58, 306, 143, 308, 217, 218, 294, 12, 77, - /* 1110 */ 22, 23, 217, 218, 194, 78, 306, 80, 308, 194, - /* 1120 */ 232, 254, 90, 27, 117, 93, 103, 104, 105, 106, - /* 1130 */ 107, 108, 109, 110, 111, 112, 113, 114, 42, 194, - /* 1140 */ 35, 194, 217, 218, 214, 194, 103, 104, 105, 106, - /* 1150 */ 107, 108, 109, 110, 111, 112, 113, 114, 194, 239, - /* 1160 */ 64, 194, 217, 218, 217, 218, 209, 210, 217, 218, - /* 1170 */ 74, 207, 67, 22, 194, 77, 19, 232, 245, 232, - /* 1180 */ 75, 24, 312, 232, 217, 218, 316, 25, 90, 256, - /* 1190 */ 102, 93, 159, 229, 161, 231, 19, 217, 218, 162, + /* 1100 */ 57, 58, 307, 195, 309, 218, 219, 263, 12, 195, + /* 1110 */ 78, 267, 80, 112, 113, 114, 307, 22, 309, 24, + /* 1120 */ 255, 281, 266, 27, 107, 108, 103, 104, 105, 106, + /* 1130 */ 107, 108, 109, 110, 111, 112, 113, 114, 42, 195, + /* 1140 */ 11, 22, 255, 24, 195, 195, 103, 104, 105, 106, + /* 1150 */ 107, 108, 109, 110, 111, 112, 113, 114, 19, 195, + /* 1160 */ 64, 195, 218, 219, 195, 313, 195, 218, 219, 317, + /* 1170 */ 74, 154, 195, 156, 195, 195, 19, 233, 23, 60, + /* 1180 */ 25, 24, 218, 219, 218, 219, 195, 218, 219, 218, + /* 1190 */ 219, 128, 129, 130, 162, 263, 19, 218, 219, 267, /* 1200 */ 43, 44, 45, 160, 47, 48, 49, 50, 51, 52, - /* 1210 */ 53, 54, 55, 56, 57, 58, 19, 23, 288, 25, - /* 1220 */ 43, 44, 45, 293, 47, 48, 49, 50, 51, 52, - /* 1230 */ 53, 54, 55, 56, 57, 58, 131, 22, 275, 24, - /* 1240 */ 43, 44, 45, 194, 47, 48, 49, 50, 51, 52, - /* 1250 */ 53, 54, 55, 56, 57, 58, 194, 194, 107, 108, + /* 1210 */ 53, 54, 55, 56, 57, 58, 19, 240, 228, 255, + /* 1220 */ 43, 44, 45, 25, 47, 48, 49, 50, 51, 52, + /* 1230 */ 53, 54, 55, 56, 57, 58, 135, 118, 137, 138, + /* 1240 */ 43, 44, 45, 22, 47, 48, 49, 50, 51, 52, + /* 1250 */ 53, 54, 55, 56, 57, 58, 117, 266, 129, 130, /* 1260 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 1270 */ 113, 114, 194, 25, 194, 60, 194, 314, 315, 194, + /* 1270 */ 113, 114, 195, 195, 119, 295, 195, 206, 195, 195, /* 1280 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 1290 */ 113, 114, 19, 194, 194, 217, 218, 217, 218, 25, + /* 1290 */ 113, 114, 195, 195, 195, 218, 219, 195, 195, 144, /* 1300 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 1310 */ 113, 114, 150, 119, 265, 312, 67, 217, 218, 316, - /* 1320 */ 19, 239, 194, 194, 128, 129, 130, 265, 265, 209, - /* 1330 */ 210, 31, 254, 118, 254, 86, 60, 194, 144, 39, - /* 1340 */ 19, 130, 275, 312, 95, 44, 45, 316, 47, 48, + /* 1310 */ 113, 114, 241, 242, 67, 218, 219, 218, 219, 146, + /* 1320 */ 19, 218, 219, 240, 215, 254, 136, 256, 107, 108, + /* 1330 */ 195, 141, 255, 86, 128, 129, 130, 195, 165, 195, + /* 1340 */ 19, 143, 95, 272, 25, 44, 45, 266, 47, 48, /* 1350 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, - /* 1360 */ 217, 218, 24, 194, 153, 12, 45, 119, 47, 48, + /* 1360 */ 218, 219, 218, 219, 195, 12, 45, 195, 47, 48, /* 1370 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, - /* 1380 */ 27, 314, 315, 22, 312, 24, 217, 218, 316, 116, - /* 1390 */ 117, 22, 144, 25, 118, 42, 147, 194, 60, 19, - /* 1400 */ 20, 127, 22, 194, 103, 104, 105, 106, 107, 108, - /* 1410 */ 109, 110, 111, 112, 113, 114, 36, 64, 145, 194, - /* 1420 */ 217, 218, 194, 54, 103, 104, 105, 106, 107, 108, - /* 1430 */ 109, 110, 111, 112, 113, 114, 194, 22, 310, 25, - /* 1440 */ 60, 194, 217, 218, 194, 217, 218, 194, 260, 194, - /* 1450 */ 301, 302, 72, 262, 262, 194, 118, 266, 266, 217, - /* 1460 */ 218, 194, 82, 245, 217, 218, 194, 217, 218, 194, - /* 1470 */ 217, 218, 217, 218, 256, 60, 24, 194, 217, 218, - /* 1480 */ 194, 101, 194, 245, 217, 218, 194, 107, 108, 217, - /* 1490 */ 218, 22, 217, 218, 256, 115, 245, 117, 118, 119, - /* 1500 */ 217, 218, 122, 217, 218, 217, 218, 256, 22, 217, - /* 1510 */ 218, 116, 258, 259, 146, 19, 20, 194, 22, 139, - /* 1520 */ 140, 150, 151, 136, 23, 194, 25, 194, 141, 194, - /* 1530 */ 194, 62, 36, 118, 154, 155, 156, 157, 158, 100, - /* 1540 */ 145, 194, 154, 194, 156, 49, 194, 23, 194, 25, - /* 1550 */ 217, 218, 217, 218, 194, 257, 60, 143, 194, 60, - /* 1560 */ 194, 121, 122, 183, 217, 218, 217, 218, 72, 217, - /* 1570 */ 218, 217, 218, 134, 23, 194, 25, 217, 218, 7, - /* 1580 */ 8, 321, 86, 217, 218, 194, 122, 91, 19, 20, - /* 1590 */ 23, 22, 25, 23, 142, 25, 132, 101, 217, 218, - /* 1600 */ 194, 194, 194, 107, 108, 36, 194, 194, 217, 218, - /* 1610 */ 194, 115, 194, 117, 118, 119, 194, 118, 122, 194, - /* 1620 */ 23, 194, 25, 217, 218, 217, 218, 194, 142, 60, - /* 1630 */ 217, 218, 194, 217, 218, 217, 218, 84, 85, 217, - /* 1640 */ 218, 72, 217, 218, 217, 218, 60, 23, 60, 25, - /* 1650 */ 154, 155, 156, 157, 158, 86, 154, 194, 156, 194, - /* 1660 */ 91, 19, 20, 23, 22, 25, 194, 23, 194, 25, - /* 1670 */ 101, 23, 194, 25, 194, 194, 107, 108, 36, 183, - /* 1680 */ 217, 218, 217, 218, 115, 194, 117, 118, 119, 217, - /* 1690 */ 218, 122, 194, 237, 194, 217, 218, 217, 218, 194, - /* 1700 */ 194, 23, 60, 25, 118, 23, 118, 25, 217, 218, - /* 1710 */ 23, 194, 25, 194, 72, 217, 218, 217, 218, 23, - /* 1720 */ 194, 25, 194, 154, 155, 156, 157, 158, 86, 23, - /* 1730 */ 194, 25, 194, 91, 217, 218, 217, 218, 194, 194, - /* 1740 */ 194, 194, 194, 101, 194, 217, 218, 290, 194, 107, - /* 1750 */ 108, 194, 183, 217, 218, 194, 194, 115, 194, 117, - /* 1760 */ 118, 119, 194, 243, 122, 194, 289, 194, 194, 194, - /* 1770 */ 194, 257, 257, 257, 244, 257, 192, 273, 215, 269, - /* 1780 */ 246, 299, 269, 295, 247, 273, 247, 246, 295, 230, - /* 1790 */ 261, 226, 261, 261, 273, 273, 154, 155, 156, 157, - /* 1800 */ 158, 0, 1, 2, 221, 220, 5, 220, 250, 220, - /* 1810 */ 261, 10, 11, 12, 13, 14, 250, 282, 17, 197, - /* 1820 */ 61, 19, 20, 246, 22, 183, 244, 142, 201, 201, - /* 1830 */ 38, 30, 299, 32, 201, 152, 22, 151, 36, 299, - /* 1840 */ 43, 40, 235, 18, 238, 285, 238, 238, 296, 238, - /* 1850 */ 201, 296, 274, 272, 18, 200, 235, 150, 235, 247, - /* 1860 */ 247, 247, 60, 247, 274, 274, 272, 201, 200, 159, - /* 1870 */ 63, 292, 71, 201, 72, 200, 22, 201, 222, 200, - /* 1880 */ 79, 201, 222, 82, 291, 116, 219, 65, 200, 219, - /* 1890 */ 219, 228, 22, 127, 225, 166, 222, 24, 225, 114, - /* 1900 */ 99, 284, 221, 101, 284, 228, 307, 315, 219, 107, - /* 1910 */ 108, 219, 219, 219, 201, 92, 320, 115, 320, 117, - /* 1920 */ 118, 119, 222, 83, 122, 267, 267, 149, 146, 19, - /* 1930 */ 20, 22, 22, 279, 250, 134, 201, 148, 159, 249, - /* 1940 */ 139, 140, 251, 141, 25, 281, 36, 147, 251, 248, - /* 1950 */ 247, 203, 13, 195, 6, 195, 154, 155, 156, 157, - /* 1960 */ 158, 193, 305, 193, 163, 193, 305, 302, 214, 208, - /* 1970 */ 60, 214, 208, 223, 223, 4, 214, 214, 3, 22, - /* 1980 */ 215, 214, 72, 208, 164, 183, 215, 15, 23, 16, - /* 1990 */ 23, 140, 131, 152, 25, 143, 20, 16, 24, 1, - /* 2000 */ 145, 143, 131, 131, 62, 37, 54, 54, 152, 54, - /* 2010 */ 54, 101, 131, 117, 34, 1, 142, 107, 108, 22, - /* 2020 */ 5, 116, 162, 69, 25, 115, 76, 117, 118, 119, - /* 2030 */ 1, 2, 122, 69, 5, 41, 142, 116, 20, 10, + /* 1380 */ 27, 23, 7, 8, 9, 210, 211, 218, 219, 116, + /* 1390 */ 218, 219, 228, 16, 147, 42, 195, 295, 195, 19, + /* 1400 */ 20, 266, 22, 294, 103, 104, 105, 106, 107, 108, + /* 1410 */ 109, 110, 111, 112, 113, 114, 36, 64, 145, 218, + /* 1420 */ 219, 218, 219, 195, 103, 104, 105, 106, 107, 108, + /* 1430 */ 109, 110, 111, 112, 113, 114, 195, 154, 119, 156, + /* 1440 */ 60, 189, 190, 191, 192, 195, 218, 219, 195, 197, + /* 1450 */ 195, 199, 72, 195, 19, 78, 195, 80, 206, 218, + /* 1460 */ 219, 195, 82, 144, 210, 211, 195, 15, 218, 219, + /* 1470 */ 47, 218, 219, 218, 219, 259, 260, 195, 261, 218, + /* 1480 */ 219, 101, 302, 303, 218, 219, 195, 107, 108, 218, + /* 1490 */ 219, 150, 151, 241, 242, 115, 25, 117, 118, 119, + /* 1500 */ 218, 219, 122, 195, 146, 195, 254, 195, 256, 218, + /* 1510 */ 219, 246, 25, 61, 246, 19, 20, 195, 22, 139, + /* 1520 */ 140, 269, 257, 195, 266, 257, 218, 219, 218, 219, + /* 1530 */ 218, 219, 36, 246, 154, 155, 156, 157, 158, 116, + /* 1540 */ 218, 219, 195, 22, 257, 49, 218, 219, 23, 195, + /* 1550 */ 25, 195, 117, 301, 195, 25, 60, 195, 195, 23, + /* 1560 */ 195, 25, 195, 183, 24, 218, 219, 130, 72, 195, + /* 1570 */ 22, 195, 218, 219, 218, 219, 195, 218, 219, 195, + /* 1580 */ 218, 219, 86, 218, 219, 218, 219, 91, 19, 20, + /* 1590 */ 153, 22, 218, 219, 218, 219, 195, 101, 195, 218, + /* 1600 */ 219, 195, 195, 107, 108, 36, 23, 195, 25, 195, + /* 1610 */ 62, 115, 195, 117, 118, 119, 195, 146, 122, 218, + /* 1620 */ 219, 218, 219, 195, 218, 219, 19, 60, 122, 60, + /* 1630 */ 218, 219, 218, 219, 195, 218, 219, 150, 132, 218, + /* 1640 */ 219, 72, 195, 23, 195, 25, 218, 219, 195, 60, + /* 1650 */ 154, 155, 156, 157, 158, 86, 23, 195, 25, 195, + /* 1660 */ 91, 19, 20, 142, 22, 218, 219, 218, 219, 130, + /* 1670 */ 101, 218, 219, 143, 121, 122, 107, 108, 36, 183, + /* 1680 */ 218, 219, 142, 60, 115, 118, 117, 118, 119, 7, + /* 1690 */ 8, 122, 153, 23, 23, 25, 25, 23, 23, 25, + /* 1700 */ 25, 23, 60, 25, 23, 98, 25, 118, 84, 85, + /* 1710 */ 23, 23, 25, 25, 72, 154, 23, 156, 25, 23, + /* 1720 */ 228, 25, 195, 154, 155, 156, 157, 158, 86, 195, + /* 1730 */ 195, 258, 195, 91, 291, 322, 195, 195, 195, 195, + /* 1740 */ 195, 118, 195, 101, 195, 195, 195, 195, 238, 107, + /* 1750 */ 108, 195, 183, 195, 195, 195, 290, 115, 195, 117, + /* 1760 */ 118, 119, 244, 195, 122, 195, 195, 195, 195, 195, + /* 1770 */ 195, 258, 258, 258, 258, 193, 245, 300, 216, 274, + /* 1780 */ 247, 270, 270, 274, 296, 296, 248, 222, 262, 198, + /* 1790 */ 262, 274, 61, 274, 248, 231, 154, 155, 156, 157, + /* 1800 */ 158, 0, 1, 2, 247, 227, 5, 221, 221, 221, + /* 1810 */ 142, 10, 11, 12, 13, 14, 262, 262, 17, 202, + /* 1820 */ 300, 19, 20, 300, 22, 183, 247, 251, 251, 245, + /* 1830 */ 202, 30, 38, 32, 202, 152, 151, 22, 36, 43, + /* 1840 */ 236, 40, 18, 202, 239, 239, 18, 239, 239, 283, + /* 1850 */ 201, 150, 236, 202, 236, 201, 159, 202, 248, 248, + /* 1860 */ 248, 248, 60, 63, 201, 275, 273, 275, 273, 275, + /* 1870 */ 22, 286, 71, 223, 72, 202, 223, 297, 297, 202, + /* 1880 */ 79, 201, 116, 82, 220, 201, 220, 220, 65, 293, + /* 1890 */ 292, 229, 22, 166, 127, 226, 24, 114, 226, 223, + /* 1900 */ 99, 222, 202, 101, 285, 92, 220, 308, 83, 107, + /* 1910 */ 108, 220, 220, 316, 220, 285, 268, 115, 229, 117, + /* 1920 */ 118, 119, 223, 321, 122, 268, 149, 146, 22, 19, + /* 1930 */ 20, 202, 22, 159, 282, 134, 321, 148, 280, 147, + /* 1940 */ 139, 140, 252, 141, 25, 204, 36, 252, 13, 251, + /* 1950 */ 196, 248, 250, 249, 196, 6, 154, 155, 156, 157, + /* 1960 */ 158, 209, 194, 194, 163, 194, 306, 306, 303, 224, + /* 1970 */ 60, 215, 215, 209, 215, 215, 215, 224, 216, 216, + /* 1980 */ 4, 209, 72, 3, 22, 183, 164, 15, 23, 16, + /* 1990 */ 23, 140, 152, 131, 25, 24, 143, 20, 16, 145, + /* 2000 */ 1, 143, 131, 62, 131, 37, 54, 152, 54, 54, + /* 2010 */ 54, 101, 131, 117, 1, 34, 142, 107, 108, 5, + /* 2020 */ 22, 116, 162, 76, 41, 115, 69, 117, 118, 119, + /* 2030 */ 1, 2, 122, 25, 5, 69, 142, 116, 20, 10, /* 2040 */ 11, 12, 13, 14, 24, 19, 17, 132, 5, 126, /* 2050 */ 22, 141, 68, 10, 11, 12, 13, 14, 22, 30, /* 2060 */ 17, 32, 22, 22, 154, 155, 156, 157, 158, 40, /* 2070 */ 23, 68, 60, 30, 24, 32, 97, 28, 22, 68, /* 2080 */ 23, 37, 34, 40, 150, 22, 25, 23, 23, 23, - /* 2090 */ 22, 98, 23, 183, 23, 117, 34, 22, 25, 89, - /* 2100 */ 71, 142, 144, 76, 34, 23, 34, 76, 79, 87, - /* 2110 */ 34, 82, 34, 94, 71, 34, 22, 24, 44, 34, - /* 2120 */ 25, 25, 79, 23, 23, 82, 143, 23, 99, 143, - /* 2130 */ 23, 22, 11, 25, 23, 25, 22, 22, 22, 1, - /* 2140 */ 23, 23, 99, 136, 22, 22, 142, 142, 142, 25, - /* 2150 */ 25, 23, 15, 1, 322, 322, 322, 322, 322, 322, - /* 2160 */ 322, 322, 322, 134, 322, 322, 322, 322, 139, 140, - /* 2170 */ 322, 322, 322, 322, 322, 322, 322, 134, 322, 322, - /* 2180 */ 322, 322, 139, 140, 322, 322, 322, 322, 322, 322, - /* 2190 */ 322, 322, 163, 322, 322, 322, 322, 322, 322, 322, - /* 2200 */ 322, 322, 322, 322, 322, 322, 163, 322, 322, 322, - /* 2210 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2220 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2230 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2240 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2250 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2260 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2270 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2280 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2290 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2300 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2310 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2320 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2330 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, - /* 2340 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, - /* 2350 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, - /* 2360 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, - /* 2370 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, - /* 2380 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, - /* 2390 */ 186, 186, 186, + /* 2090 */ 22, 98, 142, 183, 23, 23, 34, 22, 25, 89, + /* 2100 */ 71, 34, 117, 144, 34, 22, 76, 76, 79, 87, + /* 2110 */ 34, 82, 34, 44, 71, 94, 34, 23, 25, 24, + /* 2120 */ 34, 25, 79, 23, 23, 82, 23, 23, 99, 143, + /* 2130 */ 143, 22, 25, 25, 23, 22, 11, 22, 22, 25, + /* 2140 */ 23, 23, 99, 22, 22, 136, 142, 142, 142, 25, + /* 2150 */ 23, 15, 1, 1, 323, 323, 323, 323, 323, 323, + /* 2160 */ 323, 323, 323, 134, 323, 323, 323, 323, 139, 140, + /* 2170 */ 323, 323, 323, 323, 323, 323, 323, 134, 323, 323, + /* 2180 */ 323, 323, 139, 140, 323, 323, 323, 323, 323, 323, + /* 2190 */ 323, 323, 163, 323, 323, 323, 323, 323, 323, 323, + /* 2200 */ 323, 323, 323, 323, 323, 323, 163, 323, 323, 323, + /* 2210 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2220 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2230 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2240 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2250 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2260 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2270 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2280 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2290 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2300 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2310 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2320 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2330 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, + /* 2340 */ 323, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2350 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2360 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2370 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2380 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2390 */ 187, 187, 187, 187, }; #define YY_SHIFT_COUNT (582) #define YY_SHIFT_MIN (0) #define YY_SHIFT_MAX (2152) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 2029, 1801, 2043, 1380, 1380, 33, 391, 1496, 1569, 1642, - /* 10 */ 702, 702, 702, 193, 33, 33, 33, 33, 33, 0, + /* 0 */ 2029, 1801, 2043, 1380, 1380, 318, 271, 1496, 1569, 1642, + /* 10 */ 702, 702, 702, 740, 318, 318, 318, 318, 318, 0, /* 20 */ 0, 216, 1177, 702, 702, 702, 702, 702, 702, 702, - /* 30 */ 702, 702, 702, 702, 702, 702, 702, 702, 406, 406, - /* 40 */ 111, 111, 218, 447, 547, 598, 598, 260, 260, 260, - /* 50 */ 260, 40, 112, 320, 340, 445, 489, 593, 637, 741, + /* 30 */ 702, 702, 702, 702, 702, 702, 702, 702, 503, 503, + /* 40 */ 111, 111, 217, 287, 348, 610, 610, 736, 736, 736, + /* 50 */ 736, 40, 112, 320, 340, 445, 489, 593, 637, 741, /* 60 */ 785, 889, 909, 1023, 1043, 1157, 1177, 1177, 1177, 1177, /* 70 */ 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, /* 80 */ 1177, 1177, 1177, 1177, 1197, 1177, 1301, 1321, 1321, 554, @@ -175021,97 +175311,97 @@ static const unsigned short int yy_shift_ofst[] = { /* 120 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, /* 130 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, /* 140 */ 702, 702, 138, 198, 198, 198, 198, 198, 198, 198, - /* 150 */ 183, 99, 236, 292, 598, 793, 167, 598, 598, 880, - /* 160 */ 880, 598, 857, 150, 195, 195, 195, 264, 113, 113, - /* 170 */ 2207, 2207, 854, 854, 854, 751, 765, 765, 765, 765, - /* 180 */ 1096, 1096, 725, 292, 882, 904, 598, 598, 598, 598, - /* 190 */ 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, - /* 200 */ 598, 598, 598, 598, 598, 1273, 1032, 1032, 598, 147, - /* 210 */ 1098, 1098, 603, 603, 1276, 1276, 363, 2207, 2207, 2207, - /* 220 */ 2207, 2207, 2207, 2207, 469, 617, 617, 801, 336, 461, - /* 230 */ 804, 864, 615, 891, 913, 598, 598, 598, 598, 598, - /* 240 */ 598, 598, 598, 598, 598, 653, 598, 598, 598, 598, - /* 250 */ 598, 598, 598, 598, 598, 598, 598, 598, 1105, 1105, - /* 260 */ 1105, 598, 598, 598, 1194, 598, 598, 598, 1215, 1249, - /* 270 */ 598, 1353, 598, 598, 598, 598, 598, 598, 598, 598, - /* 280 */ 677, 449, 902, 1338, 1338, 1338, 1338, 1248, 902, 902, - /* 290 */ 326, 1151, 1047, 755, 749, 1371, 960, 1371, 1007, 1162, - /* 300 */ 749, 749, 1162, 749, 960, 1007, 1274, 738, 215, 1300, - /* 310 */ 1300, 1300, 1395, 1395, 1395, 1395, 1368, 1368, 1033, 1414, - /* 320 */ 1387, 1361, 1759, 1759, 1685, 1685, 1792, 1792, 1685, 1683, - /* 330 */ 1686, 1814, 1797, 1825, 1825, 1825, 1825, 1685, 1836, 1707, - /* 340 */ 1686, 1686, 1707, 1814, 1797, 1707, 1797, 1707, 1685, 1836, - /* 350 */ 1710, 1807, 1685, 1836, 1854, 1685, 1836, 1685, 1836, 1854, - /* 360 */ 1769, 1769, 1769, 1822, 1870, 1870, 1854, 1769, 1766, 1769, - /* 370 */ 1822, 1769, 1769, 1729, 1873, 1785, 1785, 1854, 1685, 1823, - /* 380 */ 1823, 1840, 1840, 1778, 1782, 1909, 1685, 1779, 1778, 1789, - /* 390 */ 1800, 1707, 1919, 1939, 1939, 1948, 1948, 1948, 2207, 2207, + /* 150 */ 183, 99, 169, 549, 610, 151, 542, 610, 610, 1017, + /* 160 */ 1017, 610, 1001, 350, 464, 464, 464, 586, 1, 1, + /* 170 */ 2207, 2207, 854, 854, 854, 465, 694, 694, 694, 694, + /* 180 */ 1096, 1096, 825, 549, 847, 904, 610, 610, 610, 610, + /* 190 */ 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, + /* 200 */ 610, 610, 610, 610, 610, 488, 947, 947, 610, 1129, + /* 210 */ 495, 495, 1139, 1139, 967, 967, 1173, 2207, 2207, 2207, + /* 220 */ 2207, 2207, 2207, 2207, 617, 765, 765, 697, 444, 708, + /* 230 */ 660, 745, 510, 663, 864, 610, 610, 610, 610, 610, + /* 240 */ 610, 610, 610, 610, 610, 188, 610, 610, 610, 610, + /* 250 */ 610, 610, 610, 610, 610, 610, 610, 610, 839, 839, + /* 260 */ 839, 610, 610, 610, 1155, 610, 610, 610, 1119, 1247, + /* 270 */ 610, 1353, 610, 610, 610, 610, 610, 610, 610, 610, + /* 280 */ 1063, 494, 1101, 291, 291, 291, 291, 1319, 1101, 1101, + /* 290 */ 775, 1221, 1375, 1452, 667, 1341, 1198, 1341, 1435, 1487, + /* 300 */ 667, 667, 1487, 667, 1198, 1435, 777, 1011, 1423, 584, + /* 310 */ 584, 584, 1273, 1273, 1273, 1273, 1471, 1471, 880, 1530, + /* 320 */ 1190, 1095, 1731, 1731, 1668, 1668, 1794, 1794, 1668, 1683, + /* 330 */ 1685, 1815, 1796, 1824, 1824, 1824, 1824, 1668, 1828, 1701, + /* 340 */ 1685, 1685, 1701, 1815, 1796, 1701, 1796, 1701, 1668, 1828, + /* 350 */ 1697, 1800, 1668, 1828, 1848, 1668, 1828, 1668, 1828, 1848, + /* 360 */ 1766, 1766, 1766, 1823, 1870, 1870, 1848, 1766, 1767, 1766, + /* 370 */ 1823, 1766, 1766, 1727, 1872, 1783, 1783, 1848, 1668, 1813, + /* 380 */ 1813, 1825, 1825, 1777, 1781, 1906, 1668, 1774, 1777, 1789, + /* 390 */ 1792, 1701, 1919, 1935, 1935, 1949, 1949, 1949, 2207, 2207, /* 400 */ 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, - /* 410 */ 2207, 2207, 2207, 69, 1037, 79, 1088, 651, 1196, 1415, - /* 420 */ 1501, 1439, 1369, 1452, 911, 1211, 1524, 1469, 1551, 1567, - /* 430 */ 1570, 1624, 1640, 1644, 1499, 1440, 1572, 1464, 1597, 275, - /* 440 */ 782, 1586, 1648, 1678, 1553, 1682, 1687, 1388, 1502, 1696, - /* 450 */ 1706, 1588, 1486, 1971, 1975, 1957, 1820, 1972, 1973, 1965, - /* 460 */ 1967, 1851, 1841, 1861, 1969, 1969, 1974, 1852, 1976, 1855, - /* 470 */ 1981, 1998, 1858, 1871, 1969, 1872, 1942, 1968, 1969, 1856, - /* 480 */ 1952, 1953, 1955, 1956, 1881, 1896, 1980, 1874, 2014, 2015, - /* 490 */ 1997, 1905, 1860, 1954, 1999, 1964, 1950, 1994, 1894, 1921, + /* 410 */ 2207, 2207, 2207, 69, 1032, 79, 357, 1377, 1206, 400, + /* 420 */ 1525, 835, 332, 1540, 1437, 1539, 1536, 1548, 1583, 1620, + /* 430 */ 1633, 1670, 1671, 1674, 1567, 1553, 1682, 1506, 1675, 1358, + /* 440 */ 1607, 1589, 1678, 1681, 1624, 1687, 1688, 1283, 1561, 1693, + /* 450 */ 1696, 1623, 1521, 1976, 1980, 1962, 1822, 1972, 1973, 1965, + /* 460 */ 1967, 1851, 1840, 1862, 1969, 1969, 1971, 1853, 1977, 1854, + /* 470 */ 1982, 1999, 1858, 1871, 1969, 1873, 1941, 1968, 1969, 1855, + /* 480 */ 1952, 1954, 1955, 1956, 1881, 1896, 1981, 1874, 2013, 2014, + /* 490 */ 1998, 1905, 1860, 1957, 2008, 1966, 1947, 1983, 1894, 1921, /* 500 */ 2020, 2018, 2026, 1915, 1923, 2028, 1984, 2036, 2040, 2047, /* 510 */ 2041, 2003, 2012, 2050, 1979, 2049, 2056, 2011, 2044, 2057, - /* 520 */ 2048, 1934, 2063, 2064, 2065, 2061, 2066, 2068, 1993, 1959, - /* 530 */ 2069, 2071, 1978, 2062, 2075, 1958, 2073, 2070, 2072, 2076, - /* 540 */ 2078, 2010, 2027, 2022, 2074, 2031, 2019, 2081, 2082, 2094, - /* 550 */ 2093, 2095, 2096, 2085, 1983, 1986, 2100, 2073, 2101, 2104, - /* 560 */ 2107, 2109, 2108, 2110, 2111, 2114, 2121, 2115, 2116, 2117, - /* 570 */ 2118, 2122, 2123, 2124, 2007, 2004, 2005, 2006, 2125, 2128, - /* 580 */ 2137, 2138, 2152, + /* 520 */ 2048, 1934, 2063, 2064, 2065, 2061, 2066, 2068, 1993, 1950, + /* 530 */ 2071, 2072, 1985, 2062, 2075, 1959, 2073, 2067, 2070, 2076, + /* 540 */ 2078, 2010, 2030, 2022, 2069, 2031, 2021, 2082, 2094, 2083, + /* 550 */ 2095, 2093, 2096, 2086, 1986, 1987, 2100, 2073, 2101, 2103, + /* 560 */ 2104, 2109, 2107, 2108, 2111, 2113, 2125, 2115, 2116, 2117, + /* 570 */ 2118, 2121, 2122, 2114, 2009, 2004, 2005, 2006, 2124, 2127, + /* 580 */ 2136, 2151, 2152, }; #define YY_REDUCE_COUNT (412) -#define YY_REDUCE_MIN (-276) -#define YY_REDUCE_MAX (1775) +#define YY_REDUCE_MIN (-277) +#define YY_REDUCE_MAX (1772) static const short yy_reduce_ofst[] = { - /* 0 */ -66, 217, -63, -177, -180, 161, 364, 64, -183, 162, - /* 10 */ 223, 367, 414, -173, 473, 514, 525, 622, 626, -207, - /* 20 */ 351, -276, -38, 693, 811, 831, 833, 888, -188, 945, - /* 30 */ 947, 416, 558, 951, 867, 287, 1078, 1080, -186, 224, - /* 40 */ -132, 42, 964, 269, 417, 796, 810, -237, -231, -237, - /* 50 */ -231, -45, -45, -45, -45, -45, -45, -45, -45, -45, - /* 60 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - /* 70 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, - /* 80 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, 895, - /* 90 */ 925, 967, 980, 1100, 1143, 1169, 1203, 1225, 1228, 1242, - /* 100 */ 1247, 1250, 1253, 1255, 1261, 1267, 1272, 1275, 1283, 1286, - /* 110 */ 1288, 1292, 1333, 1335, 1347, 1349, 1352, 1354, 1360, 1366, - /* 120 */ 1381, 1391, 1406, 1408, 1413, 1416, 1418, 1422, 1425, 1427, - /* 130 */ 1463, 1465, 1472, 1478, 1480, 1491, 1498, 1500, 1517, 1519, - /* 140 */ 1528, 1536, -45, -45, -45, -45, -45, -45, -45, -45, - /* 150 */ -45, -45, -45, 312, -158, 285, -219, 9, 166, 370, - /* 160 */ 545, 707, -45, 930, 601, 963, 1067, 792, -45, -45, - /* 170 */ -45, -45, -204, -204, -204, 369, -171, -129, 632, 678, - /* 180 */ 202, 352, -270, 412, 627, 627, -9, 122, 415, 419, - /* 190 */ -56, 248, 583, 920, 6, 261, 459, 795, 1049, 813, - /* 200 */ 1062, 1082, -161, 778, 1063, 797, 870, 1003, 1128, 443, - /* 210 */ 1031, 1072, 1191, 1192, 957, 1120, 105, 1149, 523, 933, - /* 220 */ 1218, 1238, 1254, 1251, -138, 96, 117, 146, 181, 277, - /* 230 */ 280, 421, 480, 712, 830, 850, 1085, 1099, 1129, 1209, - /* 240 */ 1323, 1331, 1336, 1364, 1407, 368, 1412, 1433, 1438, 1474, - /* 250 */ 1481, 1505, 1506, 1526, 1538, 1544, 1545, 1546, 722, 764, - /* 260 */ 856, 1547, 1548, 1550, 1188, 1554, 1557, 1561, 1298, 1260, - /* 270 */ 1562, 1456, 1564, 280, 1568, 1571, 1573, 1574, 1575, 1576, - /* 280 */ 1457, 1477, 1520, 1514, 1515, 1516, 1518, 1188, 1520, 1520, - /* 290 */ 1530, 1563, 1584, 1482, 1504, 1510, 1534, 1513, 1488, 1537, - /* 300 */ 1512, 1521, 1539, 1522, 1541, 1493, 1583, 1559, 1565, 1585, - /* 310 */ 1587, 1589, 1529, 1531, 1532, 1549, 1558, 1566, 1535, 1577, - /* 320 */ 1582, 1622, 1533, 1540, 1627, 1628, 1552, 1555, 1633, 1560, - /* 330 */ 1578, 1581, 1607, 1606, 1608, 1609, 1611, 1649, 1655, 1612, - /* 340 */ 1590, 1591, 1613, 1594, 1621, 1614, 1623, 1616, 1666, 1668, - /* 350 */ 1579, 1593, 1672, 1675, 1656, 1676, 1679, 1680, 1688, 1660, - /* 360 */ 1667, 1670, 1671, 1663, 1669, 1673, 1674, 1689, 1681, 1692, - /* 370 */ 1677, 1693, 1694, 1592, 1599, 1617, 1620, 1700, 1713, 1596, - /* 380 */ 1598, 1658, 1659, 1691, 1684, 1654, 1735, 1664, 1697, 1690, - /* 390 */ 1701, 1703, 1748, 1758, 1760, 1768, 1770, 1772, 1657, 1661, - /* 400 */ 1665, 1761, 1754, 1757, 1762, 1763, 1764, 1750, 1751, 1765, - /* 410 */ 1771, 1767, 1775, + /* 0 */ -67, 1252, -64, -178, -181, 160, 1071, 143, -184, 137, + /* 10 */ 218, 220, 222, -174, 229, 268, 272, 275, 324, -208, + /* 20 */ 242, -277, -39, 81, 537, 792, 810, 812, -189, 814, + /* 30 */ 831, 163, 865, 944, 887, 840, 964, 1077, -187, 292, + /* 40 */ -133, 274, 673, 558, 682, 795, 809, -238, -232, -238, + /* 50 */ -232, 329, 329, 329, 329, 329, 329, 329, 329, 329, + /* 60 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, + /* 70 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, + /* 80 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 557, + /* 90 */ 712, 949, 966, 969, 971, 979, 1097, 1099, 1103, 1142, + /* 100 */ 1144, 1169, 1172, 1201, 1203, 1228, 1241, 1250, 1253, 1255, + /* 110 */ 1261, 1266, 1271, 1282, 1291, 1308, 1310, 1312, 1322, 1328, + /* 120 */ 1347, 1354, 1356, 1359, 1362, 1365, 1367, 1374, 1376, 1381, + /* 130 */ 1401, 1403, 1406, 1412, 1414, 1417, 1421, 1428, 1447, 1449, + /* 140 */ 1453, 1462, 329, 329, 329, 329, 329, 329, 329, 329, + /* 150 */ 329, 329, 329, -22, -159, 475, -220, 756, 38, 501, + /* 160 */ 841, 714, 329, 118, 337, 349, 363, -56, 329, 329, + /* 170 */ 329, 329, -205, -205, -205, 687, -172, -130, -57, 790, + /* 180 */ 397, 528, -271, 136, 596, 596, 90, 316, 522, 541, + /* 190 */ -37, 715, 849, 977, 628, 856, 980, 991, 1081, 1102, + /* 200 */ 1135, 1083, -162, 208, 1258, 794, -86, 159, 41, 1109, + /* 210 */ 671, 852, 844, 932, 1175, 1254, 480, 1180, 100, 258, + /* 220 */ 1265, 1268, 1216, 1287, -139, 317, 344, 63, 339, 423, + /* 230 */ 563, 636, 676, 813, 908, 914, 950, 1078, 1084, 1098, + /* 240 */ 1363, 1384, 1407, 1439, 1464, 411, 1527, 1534, 1535, 1537, + /* 250 */ 1541, 1542, 1543, 1544, 1545, 1547, 1549, 1550, 990, 1164, + /* 260 */ 1492, 1551, 1552, 1556, 1217, 1558, 1559, 1560, 1473, 1413, + /* 270 */ 1563, 1510, 1568, 563, 1570, 1571, 1572, 1573, 1574, 1575, + /* 280 */ 1443, 1466, 1518, 1513, 1514, 1515, 1516, 1217, 1518, 1518, + /* 290 */ 1531, 1562, 1582, 1477, 1505, 1511, 1533, 1512, 1488, 1538, + /* 300 */ 1509, 1517, 1546, 1519, 1557, 1489, 1565, 1564, 1578, 1586, + /* 310 */ 1587, 1588, 1526, 1528, 1554, 1555, 1576, 1577, 1566, 1579, + /* 320 */ 1584, 1591, 1520, 1523, 1617, 1628, 1580, 1581, 1632, 1585, + /* 330 */ 1590, 1593, 1604, 1605, 1606, 1608, 1609, 1641, 1649, 1610, + /* 340 */ 1592, 1594, 1611, 1595, 1616, 1612, 1618, 1613, 1651, 1654, + /* 350 */ 1596, 1598, 1655, 1663, 1650, 1673, 1680, 1677, 1684, 1653, + /* 360 */ 1664, 1666, 1667, 1662, 1669, 1672, 1676, 1686, 1679, 1691, + /* 370 */ 1689, 1692, 1694, 1597, 1599, 1619, 1630, 1699, 1700, 1602, + /* 380 */ 1615, 1648, 1657, 1690, 1698, 1658, 1729, 1652, 1695, 1702, + /* 390 */ 1704, 1703, 1741, 1754, 1758, 1768, 1769, 1771, 1660, 1661, + /* 400 */ 1665, 1752, 1756, 1757, 1759, 1760, 1764, 1745, 1753, 1762, + /* 410 */ 1763, 1761, 1772, }; static const YYACTIONTYPE yy_default[] = { /* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254, @@ -175377,6 +175667,7 @@ static const YYCODETYPE yyFallback[] = { 0, /* ERROR => nothing */ 0, /* QNUMBER => nothing */ 0, /* SPACE => nothing */ + 0, /* COMMENT => nothing */ 0, /* ILLEGAL => nothing */ }; #endif /* YYFALLBACK */ @@ -175646,143 +175937,144 @@ static const char *const yyTokenName[] = { /* 182 */ "ERROR", /* 183 */ "QNUMBER", /* 184 */ "SPACE", - /* 185 */ "ILLEGAL", - /* 186 */ "input", - /* 187 */ "cmdlist", - /* 188 */ "ecmd", - /* 189 */ "cmdx", - /* 190 */ "explain", - /* 191 */ "cmd", - /* 192 */ "transtype", - /* 193 */ "trans_opt", - /* 194 */ "nm", - /* 195 */ "savepoint_opt", - /* 196 */ "create_table", - /* 197 */ "create_table_args", - /* 198 */ "createkw", - /* 199 */ "temp", - /* 200 */ "ifnotexists", - /* 201 */ "dbnm", - /* 202 */ "columnlist", - /* 203 */ "conslist_opt", - /* 204 */ "table_option_set", - /* 205 */ "select", - /* 206 */ "table_option", - /* 207 */ "columnname", - /* 208 */ "carglist", - /* 209 */ "typetoken", - /* 210 */ "typename", - /* 211 */ "signed", - /* 212 */ "plus_num", - /* 213 */ "minus_num", - /* 214 */ "scanpt", - /* 215 */ "scantok", - /* 216 */ "ccons", - /* 217 */ "term", - /* 218 */ "expr", - /* 219 */ "onconf", - /* 220 */ "sortorder", - /* 221 */ "autoinc", - /* 222 */ "eidlist_opt", - /* 223 */ "refargs", - /* 224 */ "defer_subclause", - /* 225 */ "generated", - /* 226 */ "refarg", - /* 227 */ "refact", - /* 228 */ "init_deferred_pred_opt", - /* 229 */ "conslist", - /* 230 */ "tconscomma", - /* 231 */ "tcons", - /* 232 */ "sortlist", - /* 233 */ "eidlist", - /* 234 */ "defer_subclause_opt", - /* 235 */ "orconf", - /* 236 */ "resolvetype", - /* 237 */ "raisetype", - /* 238 */ "ifexists", - /* 239 */ "fullname", - /* 240 */ "selectnowith", - /* 241 */ "oneselect", - /* 242 */ "wqlist", - /* 243 */ "multiselect_op", - /* 244 */ "distinct", - /* 245 */ "selcollist", - /* 246 */ "from", - /* 247 */ "where_opt", - /* 248 */ "groupby_opt", - /* 249 */ "having_opt", - /* 250 */ "orderby_opt", - /* 251 */ "limit_opt", - /* 252 */ "window_clause", - /* 253 */ "values", - /* 254 */ "nexprlist", - /* 255 */ "mvalues", - /* 256 */ "sclp", - /* 257 */ "as", - /* 258 */ "seltablist", - /* 259 */ "stl_prefix", - /* 260 */ "joinop", - /* 261 */ "on_using", - /* 262 */ "indexed_by", - /* 263 */ "exprlist", - /* 264 */ "xfullname", - /* 265 */ "idlist", - /* 266 */ "indexed_opt", - /* 267 */ "nulls", - /* 268 */ "with", - /* 269 */ "where_opt_ret", - /* 270 */ "setlist", - /* 271 */ "insert_cmd", - /* 272 */ "idlist_opt", - /* 273 */ "upsert", - /* 274 */ "returning", - /* 275 */ "filter_over", - /* 276 */ "likeop", - /* 277 */ "between_op", - /* 278 */ "in_op", - /* 279 */ "paren_exprlist", - /* 280 */ "case_operand", - /* 281 */ "case_exprlist", - /* 282 */ "case_else", - /* 283 */ "uniqueflag", - /* 284 */ "collate", - /* 285 */ "vinto", - /* 286 */ "nmnum", - /* 287 */ "trigger_decl", - /* 288 */ "trigger_cmd_list", - /* 289 */ "trigger_time", - /* 290 */ "trigger_event", - /* 291 */ "foreach_clause", - /* 292 */ "when_clause", - /* 293 */ "trigger_cmd", - /* 294 */ "trnm", - /* 295 */ "tridxby", - /* 296 */ "database_kw_opt", - /* 297 */ "key_opt", - /* 298 */ "add_column_fullname", - /* 299 */ "kwcolumn_opt", - /* 300 */ "create_vtab", - /* 301 */ "vtabarglist", - /* 302 */ "vtabarg", - /* 303 */ "vtabargtoken", - /* 304 */ "lp", - /* 305 */ "anylist", - /* 306 */ "wqitem", - /* 307 */ "wqas", - /* 308 */ "withnm", - /* 309 */ "windowdefn_list", - /* 310 */ "windowdefn", - /* 311 */ "window", - /* 312 */ "frame_opt", - /* 313 */ "part_opt", - /* 314 */ "filter_clause", - /* 315 */ "over_clause", - /* 316 */ "range_or_rows", - /* 317 */ "frame_bound", - /* 318 */ "frame_bound_s", - /* 319 */ "frame_bound_e", - /* 320 */ "frame_exclude_opt", - /* 321 */ "frame_exclude", + /* 185 */ "COMMENT", + /* 186 */ "ILLEGAL", + /* 187 */ "input", + /* 188 */ "cmdlist", + /* 189 */ "ecmd", + /* 190 */ "cmdx", + /* 191 */ "explain", + /* 192 */ "cmd", + /* 193 */ "transtype", + /* 194 */ "trans_opt", + /* 195 */ "nm", + /* 196 */ "savepoint_opt", + /* 197 */ "create_table", + /* 198 */ "create_table_args", + /* 199 */ "createkw", + /* 200 */ "temp", + /* 201 */ "ifnotexists", + /* 202 */ "dbnm", + /* 203 */ "columnlist", + /* 204 */ "conslist_opt", + /* 205 */ "table_option_set", + /* 206 */ "select", + /* 207 */ "table_option", + /* 208 */ "columnname", + /* 209 */ "carglist", + /* 210 */ "typetoken", + /* 211 */ "typename", + /* 212 */ "signed", + /* 213 */ "plus_num", + /* 214 */ "minus_num", + /* 215 */ "scanpt", + /* 216 */ "scantok", + /* 217 */ "ccons", + /* 218 */ "term", + /* 219 */ "expr", + /* 220 */ "onconf", + /* 221 */ "sortorder", + /* 222 */ "autoinc", + /* 223 */ "eidlist_opt", + /* 224 */ "refargs", + /* 225 */ "defer_subclause", + /* 226 */ "generated", + /* 227 */ "refarg", + /* 228 */ "refact", + /* 229 */ "init_deferred_pred_opt", + /* 230 */ "conslist", + /* 231 */ "tconscomma", + /* 232 */ "tcons", + /* 233 */ "sortlist", + /* 234 */ "eidlist", + /* 235 */ "defer_subclause_opt", + /* 236 */ "orconf", + /* 237 */ "resolvetype", + /* 238 */ "raisetype", + /* 239 */ "ifexists", + /* 240 */ "fullname", + /* 241 */ "selectnowith", + /* 242 */ "oneselect", + /* 243 */ "wqlist", + /* 244 */ "multiselect_op", + /* 245 */ "distinct", + /* 246 */ "selcollist", + /* 247 */ "from", + /* 248 */ "where_opt", + /* 249 */ "groupby_opt", + /* 250 */ "having_opt", + /* 251 */ "orderby_opt", + /* 252 */ "limit_opt", + /* 253 */ "window_clause", + /* 254 */ "values", + /* 255 */ "nexprlist", + /* 256 */ "mvalues", + /* 257 */ "sclp", + /* 258 */ "as", + /* 259 */ "seltablist", + /* 260 */ "stl_prefix", + /* 261 */ "joinop", + /* 262 */ "on_using", + /* 263 */ "indexed_by", + /* 264 */ "exprlist", + /* 265 */ "xfullname", + /* 266 */ "idlist", + /* 267 */ "indexed_opt", + /* 268 */ "nulls", + /* 269 */ "with", + /* 270 */ "where_opt_ret", + /* 271 */ "setlist", + /* 272 */ "insert_cmd", + /* 273 */ "idlist_opt", + /* 274 */ "upsert", + /* 275 */ "returning", + /* 276 */ "filter_over", + /* 277 */ "likeop", + /* 278 */ "between_op", + /* 279 */ "in_op", + /* 280 */ "paren_exprlist", + /* 281 */ "case_operand", + /* 282 */ "case_exprlist", + /* 283 */ "case_else", + /* 284 */ "uniqueflag", + /* 285 */ "collate", + /* 286 */ "vinto", + /* 287 */ "nmnum", + /* 288 */ "trigger_decl", + /* 289 */ "trigger_cmd_list", + /* 290 */ "trigger_time", + /* 291 */ "trigger_event", + /* 292 */ "foreach_clause", + /* 293 */ "when_clause", + /* 294 */ "trigger_cmd", + /* 295 */ "trnm", + /* 296 */ "tridxby", + /* 297 */ "database_kw_opt", + /* 298 */ "key_opt", + /* 299 */ "add_column_fullname", + /* 300 */ "kwcolumn_opt", + /* 301 */ "create_vtab", + /* 302 */ "vtabarglist", + /* 303 */ "vtabarg", + /* 304 */ "vtabargtoken", + /* 305 */ "lp", + /* 306 */ "anylist", + /* 307 */ "wqitem", + /* 308 */ "wqas", + /* 309 */ "withnm", + /* 310 */ "windowdefn_list", + /* 311 */ "windowdefn", + /* 312 */ "window", + /* 313 */ "frame_opt", + /* 314 */ "part_opt", + /* 315 */ "filter_clause", + /* 316 */ "over_clause", + /* 317 */ "range_or_rows", + /* 318 */ "frame_bound", + /* 319 */ "frame_bound_s", + /* 320 */ "frame_bound_e", + /* 321 */ "frame_exclude_opt", + /* 322 */ "frame_exclude", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -176322,98 +176614,98 @@ static void yy_destructor( ** inside the C code. */ /********* Begin destructor definitions ***************************************/ - case 205: /* select */ - case 240: /* selectnowith */ - case 241: /* oneselect */ - case 253: /* values */ - case 255: /* mvalues */ + case 206: /* select */ + case 241: /* selectnowith */ + case 242: /* oneselect */ + case 254: /* values */ + case 256: /* mvalues */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy555)); -} - break; - case 217: /* term */ - case 218: /* expr */ - case 247: /* where_opt */ - case 249: /* having_opt */ - case 269: /* where_opt_ret */ - case 280: /* case_operand */ - case 282: /* case_else */ - case 285: /* vinto */ - case 292: /* when_clause */ - case 297: /* key_opt */ - case 314: /* filter_clause */ +sqlite3SelectDelete(pParse->db, (yypminor->yy637)); +} + break; + case 218: /* term */ + case 219: /* expr */ + case 248: /* where_opt */ + case 250: /* having_opt */ + case 270: /* where_opt_ret */ + case 281: /* case_operand */ + case 283: /* case_else */ + case 286: /* vinto */ + case 293: /* when_clause */ + case 298: /* key_opt */ + case 315: /* filter_clause */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy454)); -} - break; - case 222: /* eidlist_opt */ - case 232: /* sortlist */ - case 233: /* eidlist */ - case 245: /* selcollist */ - case 248: /* groupby_opt */ - case 250: /* orderby_opt */ - case 254: /* nexprlist */ - case 256: /* sclp */ - case 263: /* exprlist */ - case 270: /* setlist */ - case 279: /* paren_exprlist */ - case 281: /* case_exprlist */ - case 313: /* part_opt */ +sqlite3ExprDelete(pParse->db, (yypminor->yy590)); +} + break; + case 223: /* eidlist_opt */ + case 233: /* sortlist */ + case 234: /* eidlist */ + case 246: /* selcollist */ + case 249: /* groupby_opt */ + case 251: /* orderby_opt */ + case 255: /* nexprlist */ + case 257: /* sclp */ + case 264: /* exprlist */ + case 271: /* setlist */ + case 280: /* paren_exprlist */ + case 282: /* case_exprlist */ + case 314: /* part_opt */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy14)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy402)); } break; - case 239: /* fullname */ - case 246: /* from */ - case 258: /* seltablist */ - case 259: /* stl_prefix */ - case 264: /* xfullname */ + case 240: /* fullname */ + case 247: /* from */ + case 259: /* seltablist */ + case 260: /* stl_prefix */ + case 265: /* xfullname */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy203)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy563)); } break; - case 242: /* wqlist */ + case 243: /* wqlist */ { -sqlite3WithDelete(pParse->db, (yypminor->yy59)); +sqlite3WithDelete(pParse->db, (yypminor->yy125)); } break; - case 252: /* window_clause */ - case 309: /* windowdefn_list */ + case 253: /* window_clause */ + case 310: /* windowdefn_list */ { -sqlite3WindowListDelete(pParse->db, (yypminor->yy211)); +sqlite3WindowListDelete(pParse->db, (yypminor->yy483)); } break; - case 265: /* idlist */ - case 272: /* idlist_opt */ + case 266: /* idlist */ + case 273: /* idlist_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy132)); +sqlite3IdListDelete(pParse->db, (yypminor->yy204)); } break; - case 275: /* filter_over */ - case 310: /* windowdefn */ - case 311: /* window */ - case 312: /* frame_opt */ - case 315: /* over_clause */ + case 276: /* filter_over */ + case 311: /* windowdefn */ + case 312: /* window */ + case 313: /* frame_opt */ + case 316: /* over_clause */ { -sqlite3WindowDelete(pParse->db, (yypminor->yy211)); +sqlite3WindowDelete(pParse->db, (yypminor->yy483)); } break; - case 288: /* trigger_cmd_list */ - case 293: /* trigger_cmd */ + case 289: /* trigger_cmd_list */ + case 294: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy427)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy319)); } break; - case 290: /* trigger_event */ + case 291: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy286).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy28).b); } break; - case 317: /* frame_bound */ - case 318: /* frame_bound_s */ - case 319: /* frame_bound_e */ + case 318: /* frame_bound */ + case 319: /* frame_bound_s */ + case 320: /* frame_bound_e */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy509).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy205).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -176715,415 +177007,415 @@ static void yy_shift( /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side ** of that rule */ static const YYCODETYPE yyRuleInfoLhs[] = { - 190, /* (0) explain ::= EXPLAIN */ - 190, /* (1) explain ::= EXPLAIN QUERY PLAN */ - 189, /* (2) cmdx ::= cmd */ - 191, /* (3) cmd ::= BEGIN transtype trans_opt */ - 192, /* (4) transtype ::= */ - 192, /* (5) transtype ::= DEFERRED */ - 192, /* (6) transtype ::= IMMEDIATE */ - 192, /* (7) transtype ::= EXCLUSIVE */ - 191, /* (8) cmd ::= COMMIT|END trans_opt */ - 191, /* (9) cmd ::= ROLLBACK trans_opt */ - 191, /* (10) cmd ::= SAVEPOINT nm */ - 191, /* (11) cmd ::= RELEASE savepoint_opt nm */ - 191, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ - 196, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ - 198, /* (14) createkw ::= CREATE */ - 200, /* (15) ifnotexists ::= */ - 200, /* (16) ifnotexists ::= IF NOT EXISTS */ - 199, /* (17) temp ::= TEMP */ - 199, /* (18) temp ::= */ - 197, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ - 197, /* (20) create_table_args ::= AS select */ - 204, /* (21) table_option_set ::= */ - 204, /* (22) table_option_set ::= table_option_set COMMA table_option */ - 206, /* (23) table_option ::= WITHOUT nm */ - 206, /* (24) table_option ::= nm */ - 207, /* (25) columnname ::= nm typetoken */ - 209, /* (26) typetoken ::= */ - 209, /* (27) typetoken ::= typename LP signed RP */ - 209, /* (28) typetoken ::= typename LP signed COMMA signed RP */ - 210, /* (29) typename ::= typename ID|STRING */ - 214, /* (30) scanpt ::= */ - 215, /* (31) scantok ::= */ - 216, /* (32) ccons ::= CONSTRAINT nm */ - 216, /* (33) ccons ::= DEFAULT scantok term */ - 216, /* (34) ccons ::= DEFAULT LP expr RP */ - 216, /* (35) ccons ::= DEFAULT PLUS scantok term */ - 216, /* (36) ccons ::= DEFAULT MINUS scantok term */ - 216, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ - 216, /* (38) ccons ::= NOT NULL onconf */ - 216, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - 216, /* (40) ccons ::= UNIQUE onconf */ - 216, /* (41) ccons ::= CHECK LP expr RP */ - 216, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ - 216, /* (43) ccons ::= defer_subclause */ - 216, /* (44) ccons ::= COLLATE ID|STRING */ - 225, /* (45) generated ::= LP expr RP */ - 225, /* (46) generated ::= LP expr RP ID */ - 221, /* (47) autoinc ::= */ - 221, /* (48) autoinc ::= AUTOINCR */ - 223, /* (49) refargs ::= */ - 223, /* (50) refargs ::= refargs refarg */ - 226, /* (51) refarg ::= MATCH nm */ - 226, /* (52) refarg ::= ON INSERT refact */ - 226, /* (53) refarg ::= ON DELETE refact */ - 226, /* (54) refarg ::= ON UPDATE refact */ - 227, /* (55) refact ::= SET NULL */ - 227, /* (56) refact ::= SET DEFAULT */ - 227, /* (57) refact ::= CASCADE */ - 227, /* (58) refact ::= RESTRICT */ - 227, /* (59) refact ::= NO ACTION */ - 224, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - 224, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - 228, /* (62) init_deferred_pred_opt ::= */ - 228, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - 228, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - 203, /* (65) conslist_opt ::= */ - 230, /* (66) tconscomma ::= COMMA */ - 231, /* (67) tcons ::= CONSTRAINT nm */ - 231, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - 231, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ - 231, /* (70) tcons ::= CHECK LP expr RP onconf */ - 231, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - 234, /* (72) defer_subclause_opt ::= */ - 219, /* (73) onconf ::= */ - 219, /* (74) onconf ::= ON CONFLICT resolvetype */ - 235, /* (75) orconf ::= */ - 235, /* (76) orconf ::= OR resolvetype */ - 236, /* (77) resolvetype ::= IGNORE */ - 236, /* (78) resolvetype ::= REPLACE */ - 191, /* (79) cmd ::= DROP TABLE ifexists fullname */ - 238, /* (80) ifexists ::= IF EXISTS */ - 238, /* (81) ifexists ::= */ - 191, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - 191, /* (83) cmd ::= DROP VIEW ifexists fullname */ - 191, /* (84) cmd ::= select */ - 205, /* (85) select ::= WITH wqlist selectnowith */ - 205, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ - 205, /* (87) select ::= selectnowith */ - 240, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ - 243, /* (89) multiselect_op ::= UNION */ - 243, /* (90) multiselect_op ::= UNION ALL */ - 243, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ - 241, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - 241, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - 253, /* (94) values ::= VALUES LP nexprlist RP */ - 241, /* (95) oneselect ::= mvalues */ - 255, /* (96) mvalues ::= values COMMA LP nexprlist RP */ - 255, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ - 244, /* (98) distinct ::= DISTINCT */ - 244, /* (99) distinct ::= ALL */ - 244, /* (100) distinct ::= */ - 256, /* (101) sclp ::= */ - 245, /* (102) selcollist ::= sclp scanpt expr scanpt as */ - 245, /* (103) selcollist ::= sclp scanpt STAR */ - 245, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ - 257, /* (105) as ::= AS nm */ - 257, /* (106) as ::= */ - 246, /* (107) from ::= */ - 246, /* (108) from ::= FROM seltablist */ - 259, /* (109) stl_prefix ::= seltablist joinop */ - 259, /* (110) stl_prefix ::= */ - 258, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ - 258, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ - 258, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ - 258, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ - 258, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ - 201, /* (116) dbnm ::= */ - 201, /* (117) dbnm ::= DOT nm */ - 239, /* (118) fullname ::= nm */ - 239, /* (119) fullname ::= nm DOT nm */ - 264, /* (120) xfullname ::= nm */ - 264, /* (121) xfullname ::= nm DOT nm */ - 264, /* (122) xfullname ::= nm DOT nm AS nm */ - 264, /* (123) xfullname ::= nm AS nm */ - 260, /* (124) joinop ::= COMMA|JOIN */ - 260, /* (125) joinop ::= JOIN_KW JOIN */ - 260, /* (126) joinop ::= JOIN_KW nm JOIN */ - 260, /* (127) joinop ::= JOIN_KW nm nm JOIN */ - 261, /* (128) on_using ::= ON expr */ - 261, /* (129) on_using ::= USING LP idlist RP */ - 261, /* (130) on_using ::= */ - 266, /* (131) indexed_opt ::= */ - 262, /* (132) indexed_by ::= INDEXED BY nm */ - 262, /* (133) indexed_by ::= NOT INDEXED */ - 250, /* (134) orderby_opt ::= */ - 250, /* (135) orderby_opt ::= ORDER BY sortlist */ - 232, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ - 232, /* (137) sortlist ::= expr sortorder nulls */ - 220, /* (138) sortorder ::= ASC */ - 220, /* (139) sortorder ::= DESC */ - 220, /* (140) sortorder ::= */ - 267, /* (141) nulls ::= NULLS FIRST */ - 267, /* (142) nulls ::= NULLS LAST */ - 267, /* (143) nulls ::= */ - 248, /* (144) groupby_opt ::= */ - 248, /* (145) groupby_opt ::= GROUP BY nexprlist */ - 249, /* (146) having_opt ::= */ - 249, /* (147) having_opt ::= HAVING expr */ - 251, /* (148) limit_opt ::= */ - 251, /* (149) limit_opt ::= LIMIT expr */ - 251, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ - 251, /* (151) limit_opt ::= LIMIT expr COMMA expr */ - 191, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ - 247, /* (153) where_opt ::= */ - 247, /* (154) where_opt ::= WHERE expr */ - 269, /* (155) where_opt_ret ::= */ - 269, /* (156) where_opt_ret ::= WHERE expr */ - 269, /* (157) where_opt_ret ::= RETURNING selcollist */ - 269, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ - 191, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ - 270, /* (160) setlist ::= setlist COMMA nm EQ expr */ - 270, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ - 270, /* (162) setlist ::= nm EQ expr */ - 270, /* (163) setlist ::= LP idlist RP EQ expr */ - 191, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - 191, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ - 273, /* (166) upsert ::= */ - 273, /* (167) upsert ::= RETURNING selcollist */ - 273, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ - 273, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ - 273, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ - 273, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ - 274, /* (172) returning ::= RETURNING selcollist */ - 271, /* (173) insert_cmd ::= INSERT orconf */ - 271, /* (174) insert_cmd ::= REPLACE */ - 272, /* (175) idlist_opt ::= */ - 272, /* (176) idlist_opt ::= LP idlist RP */ - 265, /* (177) idlist ::= idlist COMMA nm */ - 265, /* (178) idlist ::= nm */ - 218, /* (179) expr ::= LP expr RP */ - 218, /* (180) expr ::= ID|INDEXED|JOIN_KW */ - 218, /* (181) expr ::= nm DOT nm */ - 218, /* (182) expr ::= nm DOT nm DOT nm */ - 217, /* (183) term ::= NULL|FLOAT|BLOB */ - 217, /* (184) term ::= STRING */ - 217, /* (185) term ::= INTEGER */ - 218, /* (186) expr ::= VARIABLE */ - 218, /* (187) expr ::= expr COLLATE ID|STRING */ - 218, /* (188) expr ::= CAST LP expr AS typetoken RP */ - 218, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ - 218, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ - 218, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ - 218, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ - 218, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ - 218, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ - 217, /* (195) term ::= CTIME_KW */ - 218, /* (196) expr ::= LP nexprlist COMMA expr RP */ - 218, /* (197) expr ::= expr AND expr */ - 218, /* (198) expr ::= expr OR expr */ - 218, /* (199) expr ::= expr LT|GT|GE|LE expr */ - 218, /* (200) expr ::= expr EQ|NE expr */ - 218, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - 218, /* (202) expr ::= expr PLUS|MINUS expr */ - 218, /* (203) expr ::= expr STAR|SLASH|REM expr */ - 218, /* (204) expr ::= expr CONCAT expr */ - 276, /* (205) likeop ::= NOT LIKE_KW|MATCH */ - 218, /* (206) expr ::= expr likeop expr */ - 218, /* (207) expr ::= expr likeop expr ESCAPE expr */ - 218, /* (208) expr ::= expr ISNULL|NOTNULL */ - 218, /* (209) expr ::= expr NOT NULL */ - 218, /* (210) expr ::= expr IS expr */ - 218, /* (211) expr ::= expr IS NOT expr */ - 218, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ - 218, /* (213) expr ::= expr IS DISTINCT FROM expr */ - 218, /* (214) expr ::= NOT expr */ - 218, /* (215) expr ::= BITNOT expr */ - 218, /* (216) expr ::= PLUS|MINUS expr */ - 218, /* (217) expr ::= expr PTR expr */ - 277, /* (218) between_op ::= BETWEEN */ - 277, /* (219) between_op ::= NOT BETWEEN */ - 218, /* (220) expr ::= expr between_op expr AND expr */ - 278, /* (221) in_op ::= IN */ - 278, /* (222) in_op ::= NOT IN */ - 218, /* (223) expr ::= expr in_op LP exprlist RP */ - 218, /* (224) expr ::= LP select RP */ - 218, /* (225) expr ::= expr in_op LP select RP */ - 218, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ - 218, /* (227) expr ::= EXISTS LP select RP */ - 218, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ - 281, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - 281, /* (230) case_exprlist ::= WHEN expr THEN expr */ - 282, /* (231) case_else ::= ELSE expr */ - 282, /* (232) case_else ::= */ - 280, /* (233) case_operand ::= */ - 263, /* (234) exprlist ::= */ - 254, /* (235) nexprlist ::= nexprlist COMMA expr */ - 254, /* (236) nexprlist ::= expr */ - 279, /* (237) paren_exprlist ::= */ - 279, /* (238) paren_exprlist ::= LP exprlist RP */ - 191, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - 283, /* (240) uniqueflag ::= UNIQUE */ - 283, /* (241) uniqueflag ::= */ - 222, /* (242) eidlist_opt ::= */ - 222, /* (243) eidlist_opt ::= LP eidlist RP */ - 233, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ - 233, /* (245) eidlist ::= nm collate sortorder */ - 284, /* (246) collate ::= */ - 284, /* (247) collate ::= COLLATE ID|STRING */ - 191, /* (248) cmd ::= DROP INDEX ifexists fullname */ - 191, /* (249) cmd ::= VACUUM vinto */ - 191, /* (250) cmd ::= VACUUM nm vinto */ - 285, /* (251) vinto ::= INTO expr */ - 285, /* (252) vinto ::= */ - 191, /* (253) cmd ::= PRAGMA nm dbnm */ - 191, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ - 191, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - 191, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ - 191, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - 212, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ - 213, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ - 191, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - 287, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - 289, /* (262) trigger_time ::= BEFORE|AFTER */ - 289, /* (263) trigger_time ::= INSTEAD OF */ - 289, /* (264) trigger_time ::= */ - 290, /* (265) trigger_event ::= DELETE|INSERT */ - 290, /* (266) trigger_event ::= UPDATE */ - 290, /* (267) trigger_event ::= UPDATE OF idlist */ - 292, /* (268) when_clause ::= */ - 292, /* (269) when_clause ::= WHEN expr */ - 288, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - 288, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ - 294, /* (272) trnm ::= nm DOT nm */ - 295, /* (273) tridxby ::= INDEXED BY nm */ - 295, /* (274) tridxby ::= NOT INDEXED */ - 293, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - 293, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - 293, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - 293, /* (278) trigger_cmd ::= scanpt select scanpt */ - 218, /* (279) expr ::= RAISE LP IGNORE RP */ - 218, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ - 237, /* (281) raisetype ::= ROLLBACK */ - 237, /* (282) raisetype ::= ABORT */ - 237, /* (283) raisetype ::= FAIL */ - 191, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ - 191, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - 191, /* (286) cmd ::= DETACH database_kw_opt expr */ - 297, /* (287) key_opt ::= */ - 297, /* (288) key_opt ::= KEY expr */ - 191, /* (289) cmd ::= REINDEX */ - 191, /* (290) cmd ::= REINDEX nm dbnm */ - 191, /* (291) cmd ::= ANALYZE */ - 191, /* (292) cmd ::= ANALYZE nm dbnm */ - 191, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ - 191, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - 191, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ - 298, /* (296) add_column_fullname ::= fullname */ - 191, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - 191, /* (298) cmd ::= create_vtab */ - 191, /* (299) cmd ::= create_vtab LP vtabarglist RP */ - 300, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 302, /* (301) vtabarg ::= */ - 303, /* (302) vtabargtoken ::= ANY */ - 303, /* (303) vtabargtoken ::= lp anylist RP */ - 304, /* (304) lp ::= LP */ - 268, /* (305) with ::= WITH wqlist */ - 268, /* (306) with ::= WITH RECURSIVE wqlist */ - 307, /* (307) wqas ::= AS */ - 307, /* (308) wqas ::= AS MATERIALIZED */ - 307, /* (309) wqas ::= AS NOT MATERIALIZED */ - 306, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ - 308, /* (311) withnm ::= nm */ - 242, /* (312) wqlist ::= wqitem */ - 242, /* (313) wqlist ::= wqlist COMMA wqitem */ - 309, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - 310, /* (315) windowdefn ::= nm AS LP window RP */ - 311, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - 311, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - 311, /* (318) window ::= ORDER BY sortlist frame_opt */ - 311, /* (319) window ::= nm ORDER BY sortlist frame_opt */ - 311, /* (320) window ::= nm frame_opt */ - 312, /* (321) frame_opt ::= */ - 312, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - 312, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - 316, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ - 318, /* (325) frame_bound_s ::= frame_bound */ - 318, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ - 319, /* (327) frame_bound_e ::= frame_bound */ - 319, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ - 317, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ - 317, /* (330) frame_bound ::= CURRENT ROW */ - 320, /* (331) frame_exclude_opt ::= */ - 320, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ - 321, /* (333) frame_exclude ::= NO OTHERS */ - 321, /* (334) frame_exclude ::= CURRENT ROW */ - 321, /* (335) frame_exclude ::= GROUP|TIES */ - 252, /* (336) window_clause ::= WINDOW windowdefn_list */ - 275, /* (337) filter_over ::= filter_clause over_clause */ - 275, /* (338) filter_over ::= over_clause */ - 275, /* (339) filter_over ::= filter_clause */ - 315, /* (340) over_clause ::= OVER LP window RP */ - 315, /* (341) over_clause ::= OVER nm */ - 314, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ - 217, /* (343) term ::= QNUMBER */ - 186, /* (344) input ::= cmdlist */ - 187, /* (345) cmdlist ::= cmdlist ecmd */ - 187, /* (346) cmdlist ::= ecmd */ - 188, /* (347) ecmd ::= SEMI */ - 188, /* (348) ecmd ::= cmdx SEMI */ - 188, /* (349) ecmd ::= explain cmdx SEMI */ - 193, /* (350) trans_opt ::= */ - 193, /* (351) trans_opt ::= TRANSACTION */ - 193, /* (352) trans_opt ::= TRANSACTION nm */ - 195, /* (353) savepoint_opt ::= SAVEPOINT */ - 195, /* (354) savepoint_opt ::= */ - 191, /* (355) cmd ::= create_table create_table_args */ - 204, /* (356) table_option_set ::= table_option */ - 202, /* (357) columnlist ::= columnlist COMMA columnname carglist */ - 202, /* (358) columnlist ::= columnname carglist */ - 194, /* (359) nm ::= ID|INDEXED|JOIN_KW */ - 194, /* (360) nm ::= STRING */ - 209, /* (361) typetoken ::= typename */ - 210, /* (362) typename ::= ID|STRING */ - 211, /* (363) signed ::= plus_num */ - 211, /* (364) signed ::= minus_num */ - 208, /* (365) carglist ::= carglist ccons */ - 208, /* (366) carglist ::= */ - 216, /* (367) ccons ::= NULL onconf */ - 216, /* (368) ccons ::= GENERATED ALWAYS AS generated */ - 216, /* (369) ccons ::= AS generated */ - 203, /* (370) conslist_opt ::= COMMA conslist */ - 229, /* (371) conslist ::= conslist tconscomma tcons */ - 229, /* (372) conslist ::= tcons */ - 230, /* (373) tconscomma ::= */ - 234, /* (374) defer_subclause_opt ::= defer_subclause */ - 236, /* (375) resolvetype ::= raisetype */ - 240, /* (376) selectnowith ::= oneselect */ - 241, /* (377) oneselect ::= values */ - 256, /* (378) sclp ::= selcollist COMMA */ - 257, /* (379) as ::= ID|STRING */ - 266, /* (380) indexed_opt ::= indexed_by */ - 274, /* (381) returning ::= */ - 218, /* (382) expr ::= term */ - 276, /* (383) likeop ::= LIKE_KW|MATCH */ - 280, /* (384) case_operand ::= expr */ - 263, /* (385) exprlist ::= nexprlist */ - 286, /* (386) nmnum ::= plus_num */ - 286, /* (387) nmnum ::= nm */ - 286, /* (388) nmnum ::= ON */ - 286, /* (389) nmnum ::= DELETE */ - 286, /* (390) nmnum ::= DEFAULT */ - 212, /* (391) plus_num ::= INTEGER|FLOAT */ - 291, /* (392) foreach_clause ::= */ - 291, /* (393) foreach_clause ::= FOR EACH ROW */ - 294, /* (394) trnm ::= nm */ - 295, /* (395) tridxby ::= */ - 296, /* (396) database_kw_opt ::= DATABASE */ - 296, /* (397) database_kw_opt ::= */ - 299, /* (398) kwcolumn_opt ::= */ - 299, /* (399) kwcolumn_opt ::= COLUMNKW */ - 301, /* (400) vtabarglist ::= vtabarg */ - 301, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ - 302, /* (402) vtabarg ::= vtabarg vtabargtoken */ - 305, /* (403) anylist ::= */ - 305, /* (404) anylist ::= anylist LP anylist RP */ - 305, /* (405) anylist ::= anylist ANY */ - 268, /* (406) with ::= */ - 309, /* (407) windowdefn_list ::= windowdefn */ - 311, /* (408) window ::= frame_opt */ + 191, /* (0) explain ::= EXPLAIN */ + 191, /* (1) explain ::= EXPLAIN QUERY PLAN */ + 190, /* (2) cmdx ::= cmd */ + 192, /* (3) cmd ::= BEGIN transtype trans_opt */ + 193, /* (4) transtype ::= */ + 193, /* (5) transtype ::= DEFERRED */ + 193, /* (6) transtype ::= IMMEDIATE */ + 193, /* (7) transtype ::= EXCLUSIVE */ + 192, /* (8) cmd ::= COMMIT|END trans_opt */ + 192, /* (9) cmd ::= ROLLBACK trans_opt */ + 192, /* (10) cmd ::= SAVEPOINT nm */ + 192, /* (11) cmd ::= RELEASE savepoint_opt nm */ + 192, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + 197, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + 199, /* (14) createkw ::= CREATE */ + 201, /* (15) ifnotexists ::= */ + 201, /* (16) ifnotexists ::= IF NOT EXISTS */ + 200, /* (17) temp ::= TEMP */ + 200, /* (18) temp ::= */ + 198, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ + 198, /* (20) create_table_args ::= AS select */ + 205, /* (21) table_option_set ::= */ + 205, /* (22) table_option_set ::= table_option_set COMMA table_option */ + 207, /* (23) table_option ::= WITHOUT nm */ + 207, /* (24) table_option ::= nm */ + 208, /* (25) columnname ::= nm typetoken */ + 210, /* (26) typetoken ::= */ + 210, /* (27) typetoken ::= typename LP signed RP */ + 210, /* (28) typetoken ::= typename LP signed COMMA signed RP */ + 211, /* (29) typename ::= typename ID|STRING */ + 215, /* (30) scanpt ::= */ + 216, /* (31) scantok ::= */ + 217, /* (32) ccons ::= CONSTRAINT nm */ + 217, /* (33) ccons ::= DEFAULT scantok term */ + 217, /* (34) ccons ::= DEFAULT LP expr RP */ + 217, /* (35) ccons ::= DEFAULT PLUS scantok term */ + 217, /* (36) ccons ::= DEFAULT MINUS scantok term */ + 217, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ + 217, /* (38) ccons ::= NOT NULL onconf */ + 217, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + 217, /* (40) ccons ::= UNIQUE onconf */ + 217, /* (41) ccons ::= CHECK LP expr RP */ + 217, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ + 217, /* (43) ccons ::= defer_subclause */ + 217, /* (44) ccons ::= COLLATE ID|STRING */ + 226, /* (45) generated ::= LP expr RP */ + 226, /* (46) generated ::= LP expr RP ID */ + 222, /* (47) autoinc ::= */ + 222, /* (48) autoinc ::= AUTOINCR */ + 224, /* (49) refargs ::= */ + 224, /* (50) refargs ::= refargs refarg */ + 227, /* (51) refarg ::= MATCH nm */ + 227, /* (52) refarg ::= ON INSERT refact */ + 227, /* (53) refarg ::= ON DELETE refact */ + 227, /* (54) refarg ::= ON UPDATE refact */ + 228, /* (55) refact ::= SET NULL */ + 228, /* (56) refact ::= SET DEFAULT */ + 228, /* (57) refact ::= CASCADE */ + 228, /* (58) refact ::= RESTRICT */ + 228, /* (59) refact ::= NO ACTION */ + 225, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + 225, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 229, /* (62) init_deferred_pred_opt ::= */ + 229, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + 229, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 204, /* (65) conslist_opt ::= */ + 231, /* (66) tconscomma ::= COMMA */ + 232, /* (67) tcons ::= CONSTRAINT nm */ + 232, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + 232, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ + 232, /* (70) tcons ::= CHECK LP expr RP onconf */ + 232, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 235, /* (72) defer_subclause_opt ::= */ + 220, /* (73) onconf ::= */ + 220, /* (74) onconf ::= ON CONFLICT resolvetype */ + 236, /* (75) orconf ::= */ + 236, /* (76) orconf ::= OR resolvetype */ + 237, /* (77) resolvetype ::= IGNORE */ + 237, /* (78) resolvetype ::= REPLACE */ + 192, /* (79) cmd ::= DROP TABLE ifexists fullname */ + 239, /* (80) ifexists ::= IF EXISTS */ + 239, /* (81) ifexists ::= */ + 192, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + 192, /* (83) cmd ::= DROP VIEW ifexists fullname */ + 192, /* (84) cmd ::= select */ + 206, /* (85) select ::= WITH wqlist selectnowith */ + 206, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ + 206, /* (87) select ::= selectnowith */ + 241, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ + 244, /* (89) multiselect_op ::= UNION */ + 244, /* (90) multiselect_op ::= UNION ALL */ + 244, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ + 242, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + 242, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + 254, /* (94) values ::= VALUES LP nexprlist RP */ + 242, /* (95) oneselect ::= mvalues */ + 256, /* (96) mvalues ::= values COMMA LP nexprlist RP */ + 256, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ + 245, /* (98) distinct ::= DISTINCT */ + 245, /* (99) distinct ::= ALL */ + 245, /* (100) distinct ::= */ + 257, /* (101) sclp ::= */ + 246, /* (102) selcollist ::= sclp scanpt expr scanpt as */ + 246, /* (103) selcollist ::= sclp scanpt STAR */ + 246, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ + 258, /* (105) as ::= AS nm */ + 258, /* (106) as ::= */ + 247, /* (107) from ::= */ + 247, /* (108) from ::= FROM seltablist */ + 260, /* (109) stl_prefix ::= seltablist joinop */ + 260, /* (110) stl_prefix ::= */ + 259, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ + 259, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + 259, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + 259, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ + 259, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ + 202, /* (116) dbnm ::= */ + 202, /* (117) dbnm ::= DOT nm */ + 240, /* (118) fullname ::= nm */ + 240, /* (119) fullname ::= nm DOT nm */ + 265, /* (120) xfullname ::= nm */ + 265, /* (121) xfullname ::= nm DOT nm */ + 265, /* (122) xfullname ::= nm DOT nm AS nm */ + 265, /* (123) xfullname ::= nm AS nm */ + 261, /* (124) joinop ::= COMMA|JOIN */ + 261, /* (125) joinop ::= JOIN_KW JOIN */ + 261, /* (126) joinop ::= JOIN_KW nm JOIN */ + 261, /* (127) joinop ::= JOIN_KW nm nm JOIN */ + 262, /* (128) on_using ::= ON expr */ + 262, /* (129) on_using ::= USING LP idlist RP */ + 262, /* (130) on_using ::= */ + 267, /* (131) indexed_opt ::= */ + 263, /* (132) indexed_by ::= INDEXED BY nm */ + 263, /* (133) indexed_by ::= NOT INDEXED */ + 251, /* (134) orderby_opt ::= */ + 251, /* (135) orderby_opt ::= ORDER BY sortlist */ + 233, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ + 233, /* (137) sortlist ::= expr sortorder nulls */ + 221, /* (138) sortorder ::= ASC */ + 221, /* (139) sortorder ::= DESC */ + 221, /* (140) sortorder ::= */ + 268, /* (141) nulls ::= NULLS FIRST */ + 268, /* (142) nulls ::= NULLS LAST */ + 268, /* (143) nulls ::= */ + 249, /* (144) groupby_opt ::= */ + 249, /* (145) groupby_opt ::= GROUP BY nexprlist */ + 250, /* (146) having_opt ::= */ + 250, /* (147) having_opt ::= HAVING expr */ + 252, /* (148) limit_opt ::= */ + 252, /* (149) limit_opt ::= LIMIT expr */ + 252, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ + 252, /* (151) limit_opt ::= LIMIT expr COMMA expr */ + 192, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + 248, /* (153) where_opt ::= */ + 248, /* (154) where_opt ::= WHERE expr */ + 270, /* (155) where_opt_ret ::= */ + 270, /* (156) where_opt_ret ::= WHERE expr */ + 270, /* (157) where_opt_ret ::= RETURNING selcollist */ + 270, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ + 192, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + 271, /* (160) setlist ::= setlist COMMA nm EQ expr */ + 271, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ + 271, /* (162) setlist ::= nm EQ expr */ + 271, /* (163) setlist ::= LP idlist RP EQ expr */ + 192, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + 192, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 274, /* (166) upsert ::= */ + 274, /* (167) upsert ::= RETURNING selcollist */ + 274, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + 274, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + 274, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ + 274, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + 275, /* (172) returning ::= RETURNING selcollist */ + 272, /* (173) insert_cmd ::= INSERT orconf */ + 272, /* (174) insert_cmd ::= REPLACE */ + 273, /* (175) idlist_opt ::= */ + 273, /* (176) idlist_opt ::= LP idlist RP */ + 266, /* (177) idlist ::= idlist COMMA nm */ + 266, /* (178) idlist ::= nm */ + 219, /* (179) expr ::= LP expr RP */ + 219, /* (180) expr ::= ID|INDEXED|JOIN_KW */ + 219, /* (181) expr ::= nm DOT nm */ + 219, /* (182) expr ::= nm DOT nm DOT nm */ + 218, /* (183) term ::= NULL|FLOAT|BLOB */ + 218, /* (184) term ::= STRING */ + 218, /* (185) term ::= INTEGER */ + 219, /* (186) expr ::= VARIABLE */ + 219, /* (187) expr ::= expr COLLATE ID|STRING */ + 219, /* (188) expr ::= CAST LP expr AS typetoken RP */ + 219, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ + 219, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + 219, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + 219, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + 219, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + 219, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + 218, /* (195) term ::= CTIME_KW */ + 219, /* (196) expr ::= LP nexprlist COMMA expr RP */ + 219, /* (197) expr ::= expr AND expr */ + 219, /* (198) expr ::= expr OR expr */ + 219, /* (199) expr ::= expr LT|GT|GE|LE expr */ + 219, /* (200) expr ::= expr EQ|NE expr */ + 219, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 219, /* (202) expr ::= expr PLUS|MINUS expr */ + 219, /* (203) expr ::= expr STAR|SLASH|REM expr */ + 219, /* (204) expr ::= expr CONCAT expr */ + 277, /* (205) likeop ::= NOT LIKE_KW|MATCH */ + 219, /* (206) expr ::= expr likeop expr */ + 219, /* (207) expr ::= expr likeop expr ESCAPE expr */ + 219, /* (208) expr ::= expr ISNULL|NOTNULL */ + 219, /* (209) expr ::= expr NOT NULL */ + 219, /* (210) expr ::= expr IS expr */ + 219, /* (211) expr ::= expr IS NOT expr */ + 219, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ + 219, /* (213) expr ::= expr IS DISTINCT FROM expr */ + 219, /* (214) expr ::= NOT expr */ + 219, /* (215) expr ::= BITNOT expr */ + 219, /* (216) expr ::= PLUS|MINUS expr */ + 219, /* (217) expr ::= expr PTR expr */ + 278, /* (218) between_op ::= BETWEEN */ + 278, /* (219) between_op ::= NOT BETWEEN */ + 219, /* (220) expr ::= expr between_op expr AND expr */ + 279, /* (221) in_op ::= IN */ + 279, /* (222) in_op ::= NOT IN */ + 219, /* (223) expr ::= expr in_op LP exprlist RP */ + 219, /* (224) expr ::= LP select RP */ + 219, /* (225) expr ::= expr in_op LP select RP */ + 219, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ + 219, /* (227) expr ::= EXISTS LP select RP */ + 219, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ + 282, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 282, /* (230) case_exprlist ::= WHEN expr THEN expr */ + 283, /* (231) case_else ::= ELSE expr */ + 283, /* (232) case_else ::= */ + 281, /* (233) case_operand ::= */ + 264, /* (234) exprlist ::= */ + 255, /* (235) nexprlist ::= nexprlist COMMA expr */ + 255, /* (236) nexprlist ::= expr */ + 280, /* (237) paren_exprlist ::= */ + 280, /* (238) paren_exprlist ::= LP exprlist RP */ + 192, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 284, /* (240) uniqueflag ::= UNIQUE */ + 284, /* (241) uniqueflag ::= */ + 223, /* (242) eidlist_opt ::= */ + 223, /* (243) eidlist_opt ::= LP eidlist RP */ + 234, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ + 234, /* (245) eidlist ::= nm collate sortorder */ + 285, /* (246) collate ::= */ + 285, /* (247) collate ::= COLLATE ID|STRING */ + 192, /* (248) cmd ::= DROP INDEX ifexists fullname */ + 192, /* (249) cmd ::= VACUUM vinto */ + 192, /* (250) cmd ::= VACUUM nm vinto */ + 286, /* (251) vinto ::= INTO expr */ + 286, /* (252) vinto ::= */ + 192, /* (253) cmd ::= PRAGMA nm dbnm */ + 192, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 192, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 192, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 192, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 213, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ + 214, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ + 192, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 288, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 290, /* (262) trigger_time ::= BEFORE|AFTER */ + 290, /* (263) trigger_time ::= INSTEAD OF */ + 290, /* (264) trigger_time ::= */ + 291, /* (265) trigger_event ::= DELETE|INSERT */ + 291, /* (266) trigger_event ::= UPDATE */ + 291, /* (267) trigger_event ::= UPDATE OF idlist */ + 293, /* (268) when_clause ::= */ + 293, /* (269) when_clause ::= WHEN expr */ + 289, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 289, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ + 295, /* (272) trnm ::= nm DOT nm */ + 296, /* (273) tridxby ::= INDEXED BY nm */ + 296, /* (274) tridxby ::= NOT INDEXED */ + 294, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + 294, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 294, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 294, /* (278) trigger_cmd ::= scanpt select scanpt */ + 219, /* (279) expr ::= RAISE LP IGNORE RP */ + 219, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ + 238, /* (281) raisetype ::= ROLLBACK */ + 238, /* (282) raisetype ::= ABORT */ + 238, /* (283) raisetype ::= FAIL */ + 192, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ + 192, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 192, /* (286) cmd ::= DETACH database_kw_opt expr */ + 298, /* (287) key_opt ::= */ + 298, /* (288) key_opt ::= KEY expr */ + 192, /* (289) cmd ::= REINDEX */ + 192, /* (290) cmd ::= REINDEX nm dbnm */ + 192, /* (291) cmd ::= ANALYZE */ + 192, /* (292) cmd ::= ANALYZE nm dbnm */ + 192, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 192, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 192, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + 299, /* (296) add_column_fullname ::= fullname */ + 192, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 192, /* (298) cmd ::= create_vtab */ + 192, /* (299) cmd ::= create_vtab LP vtabarglist RP */ + 301, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 303, /* (301) vtabarg ::= */ + 304, /* (302) vtabargtoken ::= ANY */ + 304, /* (303) vtabargtoken ::= lp anylist RP */ + 305, /* (304) lp ::= LP */ + 269, /* (305) with ::= WITH wqlist */ + 269, /* (306) with ::= WITH RECURSIVE wqlist */ + 308, /* (307) wqas ::= AS */ + 308, /* (308) wqas ::= AS MATERIALIZED */ + 308, /* (309) wqas ::= AS NOT MATERIALIZED */ + 307, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ + 309, /* (311) withnm ::= nm */ + 243, /* (312) wqlist ::= wqitem */ + 243, /* (313) wqlist ::= wqlist COMMA wqitem */ + 310, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 311, /* (315) windowdefn ::= nm AS LP window RP */ + 312, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 312, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 312, /* (318) window ::= ORDER BY sortlist frame_opt */ + 312, /* (319) window ::= nm ORDER BY sortlist frame_opt */ + 312, /* (320) window ::= nm frame_opt */ + 313, /* (321) frame_opt ::= */ + 313, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 313, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 317, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ + 319, /* (325) frame_bound_s ::= frame_bound */ + 319, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ + 320, /* (327) frame_bound_e ::= frame_bound */ + 320, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 318, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ + 318, /* (330) frame_bound ::= CURRENT ROW */ + 321, /* (331) frame_exclude_opt ::= */ + 321, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 322, /* (333) frame_exclude ::= NO OTHERS */ + 322, /* (334) frame_exclude ::= CURRENT ROW */ + 322, /* (335) frame_exclude ::= GROUP|TIES */ + 253, /* (336) window_clause ::= WINDOW windowdefn_list */ + 276, /* (337) filter_over ::= filter_clause over_clause */ + 276, /* (338) filter_over ::= over_clause */ + 276, /* (339) filter_over ::= filter_clause */ + 316, /* (340) over_clause ::= OVER LP window RP */ + 316, /* (341) over_clause ::= OVER nm */ + 315, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ + 218, /* (343) term ::= QNUMBER */ + 187, /* (344) input ::= cmdlist */ + 188, /* (345) cmdlist ::= cmdlist ecmd */ + 188, /* (346) cmdlist ::= ecmd */ + 189, /* (347) ecmd ::= SEMI */ + 189, /* (348) ecmd ::= cmdx SEMI */ + 189, /* (349) ecmd ::= explain cmdx SEMI */ + 194, /* (350) trans_opt ::= */ + 194, /* (351) trans_opt ::= TRANSACTION */ + 194, /* (352) trans_opt ::= TRANSACTION nm */ + 196, /* (353) savepoint_opt ::= SAVEPOINT */ + 196, /* (354) savepoint_opt ::= */ + 192, /* (355) cmd ::= create_table create_table_args */ + 205, /* (356) table_option_set ::= table_option */ + 203, /* (357) columnlist ::= columnlist COMMA columnname carglist */ + 203, /* (358) columnlist ::= columnname carglist */ + 195, /* (359) nm ::= ID|INDEXED|JOIN_KW */ + 195, /* (360) nm ::= STRING */ + 210, /* (361) typetoken ::= typename */ + 211, /* (362) typename ::= ID|STRING */ + 212, /* (363) signed ::= plus_num */ + 212, /* (364) signed ::= minus_num */ + 209, /* (365) carglist ::= carglist ccons */ + 209, /* (366) carglist ::= */ + 217, /* (367) ccons ::= NULL onconf */ + 217, /* (368) ccons ::= GENERATED ALWAYS AS generated */ + 217, /* (369) ccons ::= AS generated */ + 204, /* (370) conslist_opt ::= COMMA conslist */ + 230, /* (371) conslist ::= conslist tconscomma tcons */ + 230, /* (372) conslist ::= tcons */ + 231, /* (373) tconscomma ::= */ + 235, /* (374) defer_subclause_opt ::= defer_subclause */ + 237, /* (375) resolvetype ::= raisetype */ + 241, /* (376) selectnowith ::= oneselect */ + 242, /* (377) oneselect ::= values */ + 257, /* (378) sclp ::= selcollist COMMA */ + 258, /* (379) as ::= ID|STRING */ + 267, /* (380) indexed_opt ::= indexed_by */ + 275, /* (381) returning ::= */ + 219, /* (382) expr ::= term */ + 277, /* (383) likeop ::= LIKE_KW|MATCH */ + 281, /* (384) case_operand ::= expr */ + 264, /* (385) exprlist ::= nexprlist */ + 287, /* (386) nmnum ::= plus_num */ + 287, /* (387) nmnum ::= nm */ + 287, /* (388) nmnum ::= ON */ + 287, /* (389) nmnum ::= DELETE */ + 287, /* (390) nmnum ::= DEFAULT */ + 213, /* (391) plus_num ::= INTEGER|FLOAT */ + 292, /* (392) foreach_clause ::= */ + 292, /* (393) foreach_clause ::= FOR EACH ROW */ + 295, /* (394) trnm ::= nm */ + 296, /* (395) tridxby ::= */ + 297, /* (396) database_kw_opt ::= DATABASE */ + 297, /* (397) database_kw_opt ::= */ + 300, /* (398) kwcolumn_opt ::= */ + 300, /* (399) kwcolumn_opt ::= COLUMNKW */ + 302, /* (400) vtabarglist ::= vtabarg */ + 302, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ + 303, /* (402) vtabarg ::= vtabarg vtabargtoken */ + 306, /* (403) anylist ::= */ + 306, /* (404) anylist ::= anylist LP anylist RP */ + 306, /* (405) anylist ::= anylist ANY */ + 269, /* (406) with ::= */ + 310, /* (407) windowdefn_list ::= windowdefn */ + 312, /* (408) window ::= frame_opt */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -177589,16 +177881,16 @@ static YYACTIONTYPE yy_reduce( { sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy144);} +{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy502);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy144 = TK_DEFERRED;} +{yymsp[1].minor.yy502 = TK_DEFERRED;} break; case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324); -{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/} +{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -177621,7 +177913,7 @@ static YYACTIONTYPE yy_reduce( break; case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy144,0,0,yymsp[-2].minor.yy144); + sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy502,0,0,yymsp[-2].minor.yy502); } break; case 14: /* createkw ::= CREATE */ @@ -177635,38 +177927,38 @@ static YYACTIONTYPE yy_reduce( case 81: /* ifexists ::= */ yytestcase(yyruleno==81); case 100: /* distinct ::= */ yytestcase(yyruleno==100); case 246: /* collate ::= */ yytestcase(yyruleno==246); -{yymsp[1].minor.yy144 = 0;} +{yymsp[1].minor.yy502 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy144 = 1;} +{yymsp[-2].minor.yy502 = 1;} break; case 17: /* temp ::= TEMP */ -{yymsp[0].minor.yy144 = pParse->db->init.busy==0;} +{yymsp[0].minor.yy502 = pParse->db->init.busy==0;} break; case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */ { - sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy391,0); + sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy9,0); } break; case 20: /* create_table_args ::= AS select */ { - sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy555); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); + sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy637); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637); } break; case 21: /* table_option_set ::= */ -{yymsp[1].minor.yy391 = 0;} +{yymsp[1].minor.yy9 = 0;} break; case 22: /* table_option_set ::= table_option_set COMMA table_option */ -{yylhsminor.yy391 = yymsp[-2].minor.yy391|yymsp[0].minor.yy391;} - yymsp[-2].minor.yy391 = yylhsminor.yy391; +{yylhsminor.yy9 = yymsp[-2].minor.yy9|yymsp[0].minor.yy9;} + yymsp[-2].minor.yy9 = yylhsminor.yy9; break; case 23: /* table_option ::= WITHOUT nm */ { if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ - yymsp[-1].minor.yy391 = TF_WithoutRowid | TF_NoVisibleRowid; + yymsp[-1].minor.yy9 = TF_WithoutRowid | TF_NoVisibleRowid; }else{ - yymsp[-1].minor.yy391 = 0; + yymsp[-1].minor.yy9 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } @@ -177674,13 +177966,13 @@ static YYACTIONTYPE yy_reduce( case 24: /* table_option ::= nm */ { if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){ - yylhsminor.yy391 = TF_Strict; + yylhsminor.yy9 = TF_Strict; }else{ - yylhsminor.yy391 = 0; + yylhsminor.yy9 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } - yymsp[0].minor.yy391 = yylhsminor.yy391; + yymsp[0].minor.yy9 = yylhsminor.yy9; break; case 25: /* columnname ::= nm typetoken */ {sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);} @@ -177706,7 +177998,7 @@ static YYACTIONTYPE yy_reduce( case 30: /* scanpt ::= */ { assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy168 = yyLookaheadToken.z; + yymsp[1].minor.yy342 = yyLookaheadToken.z; } break; case 31: /* scantok ::= */ @@ -177720,17 +178012,17 @@ static YYACTIONTYPE yy_reduce( {pParse->constraintName = yymsp[0].minor.yy0;} break; case 33: /* ccons ::= DEFAULT scantok term */ -{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} +{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; case 34: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} break; case 35: /* ccons ::= DEFAULT PLUS scantok term */ -{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} +{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; case 36: /* ccons ::= DEFAULT MINUS scantok term */ { - Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy454, 0); + Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy590, 0); sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]); } break; @@ -177745,133 +178037,133 @@ static YYACTIONTYPE yy_reduce( } break; case 38: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy144);} +{sqlite3AddNotNull(pParse, yymsp[0].minor.yy502);} break; case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy144,yymsp[0].minor.yy144,yymsp[-2].minor.yy144);} +{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy502,yymsp[0].minor.yy502,yymsp[-2].minor.yy502);} break; case 40: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy144,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy502,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 41: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} +{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} break; case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy144);} +{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy402,yymsp[0].minor.yy502);} break; case 43: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy144);} +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy502);} break; case 44: /* ccons ::= COLLATE ID|STRING */ {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; case 45: /* generated ::= LP expr RP */ -{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy454,0);} +{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy590,0);} break; case 46: /* generated ::= LP expr RP ID */ -{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy454,&yymsp[0].minor.yy0);} +{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy590,&yymsp[0].minor.yy0);} break; case 48: /* autoinc ::= AUTOINCR */ -{yymsp[0].minor.yy144 = 1;} +{yymsp[0].minor.yy502 = 1;} break; case 49: /* refargs ::= */ -{ yymsp[1].minor.yy144 = OE_None*0x0101; /* EV: R-19803-45884 */} +{ yymsp[1].minor.yy502 = OE_None*0x0101; /* EV: R-19803-45884 */} break; case 50: /* refargs ::= refargs refarg */ -{ yymsp[-1].minor.yy144 = (yymsp[-1].minor.yy144 & ~yymsp[0].minor.yy383.mask) | yymsp[0].minor.yy383.value; } +{ yymsp[-1].minor.yy502 = (yymsp[-1].minor.yy502 & ~yymsp[0].minor.yy481.mask) | yymsp[0].minor.yy481.value; } break; case 51: /* refarg ::= MATCH nm */ -{ yymsp[-1].minor.yy383.value = 0; yymsp[-1].minor.yy383.mask = 0x000000; } +{ yymsp[-1].minor.yy481.value = 0; yymsp[-1].minor.yy481.mask = 0x000000; } break; case 52: /* refarg ::= ON INSERT refact */ -{ yymsp[-2].minor.yy383.value = 0; yymsp[-2].minor.yy383.mask = 0x000000; } +{ yymsp[-2].minor.yy481.value = 0; yymsp[-2].minor.yy481.mask = 0x000000; } break; case 53: /* refarg ::= ON DELETE refact */ -{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144; yymsp[-2].minor.yy383.mask = 0x0000ff; } +{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502; yymsp[-2].minor.yy481.mask = 0x0000ff; } break; case 54: /* refarg ::= ON UPDATE refact */ -{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144<<8; yymsp[-2].minor.yy383.mask = 0x00ff00; } +{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502<<8; yymsp[-2].minor.yy481.mask = 0x00ff00; } break; case 55: /* refact ::= SET NULL */ -{ yymsp[-1].minor.yy144 = OE_SetNull; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy502 = OE_SetNull; /* EV: R-33326-45252 */} break; case 56: /* refact ::= SET DEFAULT */ -{ yymsp[-1].minor.yy144 = OE_SetDflt; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy502 = OE_SetDflt; /* EV: R-33326-45252 */} break; case 57: /* refact ::= CASCADE */ -{ yymsp[0].minor.yy144 = OE_Cascade; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy502 = OE_Cascade; /* EV: R-33326-45252 */} break; case 58: /* refact ::= RESTRICT */ -{ yymsp[0].minor.yy144 = OE_Restrict; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy502 = OE_Restrict; /* EV: R-33326-45252 */} break; case 59: /* refact ::= NO ACTION */ -{ yymsp[-1].minor.yy144 = OE_None; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy502 = OE_None; /* EV: R-33326-45252 */} break; case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -{yymsp[-2].minor.yy144 = 0;} +{yymsp[-2].minor.yy502 = 0;} break; case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76); case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173); -{yymsp[-1].minor.yy144 = yymsp[0].minor.yy144;} +{yymsp[-1].minor.yy502 = yymsp[0].minor.yy502;} break; case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219); case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222); case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247); -{yymsp[-1].minor.yy144 = 1;} +{yymsp[-1].minor.yy502 = 1;} break; case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yymsp[-1].minor.yy144 = 0;} +{yymsp[-1].minor.yy502 = 0;} break; case 66: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 0;} break; case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy144,yymsp[-2].minor.yy144,0);} +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy402,yymsp[0].minor.yy502,yymsp[-2].minor.yy502,0);} break; case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy144,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy402,yymsp[0].minor.yy502,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 70: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy454,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} +{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy590,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} break; case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ { - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy144); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy144); + sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy402, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[-1].minor.yy502); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy502); } break; case 73: /* onconf ::= */ case 75: /* orconf ::= */ yytestcase(yyruleno==75); -{yymsp[1].minor.yy144 = OE_Default;} +{yymsp[1].minor.yy502 = OE_Default;} break; case 74: /* onconf ::= ON CONFLICT resolvetype */ -{yymsp[-2].minor.yy144 = yymsp[0].minor.yy144;} +{yymsp[-2].minor.yy502 = yymsp[0].minor.yy502;} break; case 77: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy144 = OE_Ignore;} +{yymsp[0].minor.yy502 = OE_Ignore;} break; case 78: /* resolvetype ::= REPLACE */ case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174); -{yymsp[0].minor.yy144 = OE_Replace;} +{yymsp[0].minor.yy502 = OE_Replace;} break; case 79: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy203, 0, yymsp[-1].minor.yy144); + sqlite3DropTable(pParse, yymsp[0].minor.yy563, 0, yymsp[-1].minor.yy502); } break; case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { - sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[0].minor.yy555, yymsp[-7].minor.yy144, yymsp[-5].minor.yy144); + sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[0].minor.yy637, yymsp[-7].minor.yy502, yymsp[-5].minor.yy502); } break; case 83: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144); + sqlite3DropTable(pParse, yymsp[0].minor.yy563, 1, yymsp[-1].minor.yy502); } break; case 84: /* cmd ::= select */ @@ -177880,20 +178172,20 @@ static YYACTIONTYPE yy_reduce( if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0 || sqlite3ReadSchema(pParse)==SQLITE_OK ){ - sqlite3Select(pParse, yymsp[0].minor.yy555, &dest); + sqlite3Select(pParse, yymsp[0].minor.yy637, &dest); } - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637); } break; case 85: /* select ::= WITH wqlist selectnowith */ -{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} +{yymsp[-2].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);} break; case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */ -{yymsp[-3].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} +{yymsp[-3].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);} break; case 87: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy555; + Select *p = yymsp[0].minor.yy637; if( p ){ parserDoubleLinkSelect(pParse, p); } @@ -177901,8 +178193,8 @@ static YYACTIONTYPE yy_reduce( break; case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - Select *pRhs = yymsp[0].minor.yy555; - Select *pLhs = yymsp[-2].minor.yy555; + Select *pRhs = yymsp[0].minor.yy637; + Select *pLhs = yymsp[-2].minor.yy637; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; @@ -177912,60 +178204,60 @@ static YYACTIONTYPE yy_reduce( pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ - pRhs->op = (u8)yymsp[-1].minor.yy144; + pRhs->op = (u8)yymsp[-1].minor.yy502; pRhs->pPrior = pLhs; if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; pRhs->selFlags &= ~SF_MultiValue; - if( yymsp[-1].minor.yy144!=TK_ALL ) pParse->hasCompound = 1; + if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy555 = pRhs; + yymsp[-2].minor.yy637 = pRhs; } break; case 89: /* multiselect_op ::= UNION */ case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91); -{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-OP*/} +{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-OP*/} break; case 90: /* multiselect_op ::= UNION ALL */ -{yymsp[-1].minor.yy144 = TK_ALL;} +{yymsp[-1].minor.yy502 = TK_ALL;} break; case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yymsp[-8].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy203,yymsp[-4].minor.yy454,yymsp[-3].minor.yy14,yymsp[-2].minor.yy454,yymsp[-1].minor.yy14,yymsp[-7].minor.yy144,yymsp[0].minor.yy454); + yymsp[-8].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy402,yymsp[-5].minor.yy563,yymsp[-4].minor.yy590,yymsp[-3].minor.yy402,yymsp[-2].minor.yy590,yymsp[-1].minor.yy402,yymsp[-7].minor.yy502,yymsp[0].minor.yy590); } break; case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ { - yymsp[-9].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy14,yymsp[-6].minor.yy203,yymsp[-5].minor.yy454,yymsp[-4].minor.yy14,yymsp[-3].minor.yy454,yymsp[-1].minor.yy14,yymsp[-8].minor.yy144,yymsp[0].minor.yy454); - if( yymsp[-9].minor.yy555 ){ - yymsp[-9].minor.yy555->pWinDefn = yymsp[-2].minor.yy211; + yymsp[-9].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy402,yymsp[-6].minor.yy563,yymsp[-5].minor.yy590,yymsp[-4].minor.yy402,yymsp[-3].minor.yy590,yymsp[-1].minor.yy402,yymsp[-8].minor.yy502,yymsp[0].minor.yy590); + if( yymsp[-9].minor.yy637 ){ + yymsp[-9].minor.yy637->pWinDefn = yymsp[-2].minor.yy483; }else{ - sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy211); + sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy483); } } break; case 94: /* values ::= VALUES LP nexprlist RP */ { - yymsp[-3].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0); + yymsp[-3].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy402,0,0,0,0,0,SF_Values,0); } break; case 95: /* oneselect ::= mvalues */ { - sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy555); + sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy637); } break; case 96: /* mvalues ::= values COMMA LP nexprlist RP */ case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97); { - yymsp[-4].minor.yy555 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy555, yymsp[-1].minor.yy14); + yymsp[-4].minor.yy637 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy637, yymsp[-1].minor.yy402); } break; case 98: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy144 = SF_Distinct;} +{yymsp[0].minor.yy502 = SF_Distinct;} break; case 99: /* distinct ::= ALL */ -{yymsp[0].minor.yy144 = SF_All;} +{yymsp[0].minor.yy502 = SF_All;} break; case 101: /* sclp ::= */ case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134); @@ -177973,20 +178265,20 @@ static YYACTIONTYPE yy_reduce( case 234: /* exprlist ::= */ yytestcase(yyruleno==234); case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237); case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242); -{yymsp[1].minor.yy14 = 0;} +{yymsp[1].minor.yy402 = 0;} break; case 102: /* selcollist ::= sclp scanpt expr scanpt as */ { - yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[-2].minor.yy454); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy14,yymsp[-3].minor.yy168,yymsp[-1].minor.yy168); + yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[-2].minor.yy590); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy402,yymsp[-3].minor.yy342,yymsp[-1].minor.yy342); } break; case 103: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); - yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, p); + yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy402, p); } break; case 104: /* selcollist ::= sclp scanpt nm DOT STAR */ @@ -177996,7 +178288,7 @@ static YYACTIONTYPE yy_reduce( sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, pDot); + yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, pDot); } break; case 105: /* as ::= AS nm */ @@ -178007,50 +178299,50 @@ static YYACTIONTYPE yy_reduce( break; case 107: /* from ::= */ case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110); -{yymsp[1].minor.yy203 = 0;} +{yymsp[1].minor.yy563 = 0;} break; case 108: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy203 = yymsp[0].minor.yy203; - sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy203); + yymsp[-1].minor.yy563 = yymsp[0].minor.yy563; + sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy563); } break; case 109: /* stl_prefix ::= seltablist joinop */ { - if( ALWAYS(yymsp[-1].minor.yy203 && yymsp[-1].minor.yy203->nSrc>0) ) yymsp[-1].minor.yy203->a[yymsp[-1].minor.yy203->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy144; + if( ALWAYS(yymsp[-1].minor.yy563 && yymsp[-1].minor.yy563->nSrc>0) ) yymsp[-1].minor.yy563->a[yymsp[-1].minor.yy563->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy502; } break; case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */ { - yymsp[-4].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy203,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); + yymsp[-4].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy563,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); } break; case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ { - yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy269); - sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-1].minor.yy0); + yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy421); + sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-1].minor.yy0); } break; case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ { - yymsp[-7].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy203,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); - sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy203, yymsp[-3].minor.yy14); + yymsp[-7].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy563,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); + sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy563, yymsp[-3].minor.yy402); } break; case 114: /* seltablist ::= stl_prefix LP select RP as on_using */ { - yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy555,&yymsp[0].minor.yy269); + yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy637,&yymsp[0].minor.yy421); } break; case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ { - if( yymsp[-5].minor.yy203==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy269.pOn==0 && yymsp[0].minor.yy269.pUsing==0 ){ - yymsp[-5].minor.yy203 = yymsp[-3].minor.yy203; - }else if( ALWAYS(yymsp[-3].minor.yy203!=0) && yymsp[-3].minor.yy203->nSrc==1 ){ - yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); - if( yymsp[-5].minor.yy203 ){ - SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1]; - SrcItem *pOld = yymsp[-3].minor.yy203->a; + if( yymsp[-5].minor.yy563==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy421.pOn==0 && yymsp[0].minor.yy421.pUsing==0 ){ + yymsp[-5].minor.yy563 = yymsp[-3].minor.yy563; + }else if( ALWAYS(yymsp[-3].minor.yy563!=0) && yymsp[-3].minor.yy563->nSrc==1 ){ + yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); + if( yymsp[-5].minor.yy563 ){ + SrcItem *pNew = &yymsp[-5].minor.yy563->a[yymsp[-5].minor.yy563->nSrc-1]; + SrcItem *pOld = yymsp[-3].minor.yy563->a; assert( pOld->fg.fixedSchema==0 ); pNew->zName = pOld->zName; assert( pOld->fg.fixedSchema==0 ); @@ -178075,12 +178367,12 @@ static YYACTIONTYPE yy_reduce( } pOld->zName = 0; } - sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203); + sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy563); }else{ Select *pSubquery; - sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy203); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy203,0,0,0,0,SF_NestedFrom,0); - yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy269); + sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy563); + pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy563,0,0,0,0,SF_NestedFrom,0); + yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy421); } } break; @@ -178090,56 +178382,56 @@ static YYACTIONTYPE yy_reduce( break; case 118: /* fullname ::= nm */ { - yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); - if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy203 = yylhsminor.yy203; + yymsp[0].minor.yy563 = yylhsminor.yy563; break; case 119: /* fullname ::= nm DOT nm */ { - yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); - if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy203 = yylhsminor.yy203; + yymsp[-2].minor.yy563 = yylhsminor.yy563; break; case 120: /* xfullname ::= nm */ -{yymsp[0].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} +{yymsp[0].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} break; case 121: /* xfullname ::= nm DOT nm */ -{yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 122: /* xfullname ::= nm DOT nm AS nm */ { - yymsp[-4].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ - if( yymsp[-4].minor.yy203 ) yymsp[-4].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-4].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ + if( yymsp[-4].minor.yy563 ) yymsp[-4].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; case 123: /* xfullname ::= nm AS nm */ { - yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ - if( yymsp[-2].minor.yy203 ) yymsp[-2].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ + if( yymsp[-2].minor.yy563 ) yymsp[-2].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; case 124: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy144 = JT_INNER; } +{ yymsp[0].minor.yy502 = JT_INNER; } break; case 125: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} +{yymsp[-1].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; case 126: /* joinop ::= JOIN_KW nm JOIN */ -{yymsp[-2].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} +{yymsp[-2].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; case 127: /* joinop ::= JOIN_KW nm nm JOIN */ -{yymsp[-3].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} +{yymsp[-3].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; case 128: /* on_using ::= ON expr */ -{yymsp[-1].minor.yy269.pOn = yymsp[0].minor.yy454; yymsp[-1].minor.yy269.pUsing = 0;} +{yymsp[-1].minor.yy421.pOn = yymsp[0].minor.yy590; yymsp[-1].minor.yy421.pUsing = 0;} break; case 129: /* on_using ::= USING LP idlist RP */ -{yymsp[-3].minor.yy269.pOn = 0; yymsp[-3].minor.yy269.pUsing = yymsp[-1].minor.yy132;} +{yymsp[-3].minor.yy421.pOn = 0; yymsp[-3].minor.yy421.pUsing = yymsp[-1].minor.yy204;} break; case 130: /* on_using ::= */ -{yymsp[1].minor.yy269.pOn = 0; yymsp[1].minor.yy269.pUsing = 0;} +{yymsp[1].minor.yy421.pOn = 0; yymsp[1].minor.yy421.pUsing = 0;} break; case 132: /* indexed_by ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} @@ -178149,35 +178441,35 @@ static YYACTIONTYPE yy_reduce( break; case 135: /* orderby_opt ::= ORDER BY sortlist */ case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145); -{yymsp[-2].minor.yy14 = yymsp[0].minor.yy14;} +{yymsp[-2].minor.yy402 = yymsp[0].minor.yy402;} break; case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */ { - yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14,yymsp[-2].minor.yy454); - sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144); + yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402,yymsp[-2].minor.yy590); + sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502); } break; case 137: /* sortlist ::= expr sortorder nulls */ { - yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy454); /*A-overwrites-Y*/ - sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144); + yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy590); /*A-overwrites-Y*/ + sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502); } break; case 138: /* sortorder ::= ASC */ -{yymsp[0].minor.yy144 = SQLITE_SO_ASC;} +{yymsp[0].minor.yy502 = SQLITE_SO_ASC;} break; case 139: /* sortorder ::= DESC */ -{yymsp[0].minor.yy144 = SQLITE_SO_DESC;} +{yymsp[0].minor.yy502 = SQLITE_SO_DESC;} break; case 140: /* sortorder ::= */ case 143: /* nulls ::= */ yytestcase(yyruleno==143); -{yymsp[1].minor.yy144 = SQLITE_SO_UNDEFINED;} +{yymsp[1].minor.yy502 = SQLITE_SO_UNDEFINED;} break; case 141: /* nulls ::= NULLS FIRST */ -{yymsp[-1].minor.yy144 = SQLITE_SO_ASC;} +{yymsp[-1].minor.yy502 = SQLITE_SO_ASC;} break; case 142: /* nulls ::= NULLS LAST */ -{yymsp[-1].minor.yy144 = SQLITE_SO_DESC;} +{yymsp[-1].minor.yy502 = SQLITE_SO_DESC;} break; case 146: /* having_opt ::= */ case 148: /* limit_opt ::= */ yytestcase(yyruleno==148); @@ -178186,42 +178478,42 @@ static YYACTIONTYPE yy_reduce( case 232: /* case_else ::= */ yytestcase(yyruleno==232); case 233: /* case_operand ::= */ yytestcase(yyruleno==233); case 252: /* vinto ::= */ yytestcase(yyruleno==252); -{yymsp[1].minor.yy454 = 0;} +{yymsp[1].minor.yy590 = 0;} break; case 147: /* having_opt ::= HAVING expr */ case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154); case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156); case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231); case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251); -{yymsp[-1].minor.yy454 = yymsp[0].minor.yy454;} +{yymsp[-1].minor.yy590 = yymsp[0].minor.yy590;} break; case 149: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,0);} +{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,0);} break; case 150: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} +{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} break; case 151: /* limit_opt ::= LIMIT expr COMMA expr */ -{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,yymsp[-2].minor.yy454);} +{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,yymsp[-2].minor.yy590);} break; case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy203, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy203,yymsp[0].minor.yy454,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy563, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy563,yymsp[0].minor.yy590,0,0); } break; case 157: /* where_opt_ret ::= RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-1].minor.yy454 = 0;} +{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-1].minor.yy590 = 0;} break; case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-3].minor.yy454 = yymsp[-2].minor.yy454;} +{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-3].minor.yy590 = yymsp[-2].minor.yy590;} break; case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-4].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy14,"set list"); - if( yymsp[-1].minor.yy203 ){ - SrcList *pFromClause = yymsp[-1].minor.yy203; + sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-4].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy402,"set list"); + if( yymsp[-1].minor.yy563 ){ + SrcList *pFromClause = yymsp[-1].minor.yy563; if( pFromClause->nSrc>1 ){ Select *pSubquery; Token as; @@ -178230,90 +178522,90 @@ static YYACTIONTYPE yy_reduce( as.z = 0; pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); } - yymsp[-5].minor.yy203 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy203, pFromClause); + yymsp[-5].minor.yy563 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy563, pFromClause); } - sqlite3Update(pParse,yymsp[-5].minor.yy203,yymsp[-2].minor.yy14,yymsp[0].minor.yy454,yymsp[-6].minor.yy144,0,0,0); + sqlite3Update(pParse,yymsp[-5].minor.yy563,yymsp[-2].minor.yy402,yymsp[0].minor.yy590,yymsp[-6].minor.yy502,0,0,0); } break; case 160: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy454); - sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, 1); + yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[0].minor.yy590); + sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, 1); } break; case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-6].minor.yy14 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy14, yymsp[-3].minor.yy132, yymsp[0].minor.yy454); + yymsp[-6].minor.yy402 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy402, yymsp[-3].minor.yy204, yymsp[0].minor.yy590); } break; case 162: /* setlist ::= nm EQ expr */ { - yylhsminor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy454); - sqlite3ExprListSetName(pParse, yylhsminor.yy14, &yymsp[-2].minor.yy0, 1); + yylhsminor.yy402 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy590); + sqlite3ExprListSetName(pParse, yylhsminor.yy402, &yymsp[-2].minor.yy0, 1); } - yymsp[-2].minor.yy14 = yylhsminor.yy14; + yymsp[-2].minor.yy402 = yylhsminor.yy402; break; case 163: /* setlist ::= LP idlist RP EQ expr */ { - yymsp[-4].minor.yy14 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy132, yymsp[0].minor.yy454); + yymsp[-4].minor.yy402 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy204, yymsp[0].minor.yy590); } break; case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy555, yymsp[-2].minor.yy132, yymsp[-5].minor.yy144, yymsp[0].minor.yy122); + sqlite3Insert(pParse, yymsp[-3].minor.yy563, yymsp[-1].minor.yy637, yymsp[-2].minor.yy204, yymsp[-5].minor.yy502, yymsp[0].minor.yy403); } break; case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ { - sqlite3Insert(pParse, yymsp[-4].minor.yy203, 0, yymsp[-3].minor.yy132, yymsp[-6].minor.yy144, 0); + sqlite3Insert(pParse, yymsp[-4].minor.yy563, 0, yymsp[-3].minor.yy204, yymsp[-6].minor.yy502, 0); } break; case 166: /* upsert ::= */ -{ yymsp[1].minor.yy122 = 0; } +{ yymsp[1].minor.yy403 = 0; } break; case 167: /* upsert ::= RETURNING selcollist */ -{ yymsp[-1].minor.yy122 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy14); } +{ yymsp[-1].minor.yy403 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy402); } break; case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ -{ yymsp[-11].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy14,yymsp[-6].minor.yy454,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,yymsp[0].minor.yy122);} +{ yymsp[-11].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy402,yymsp[-6].minor.yy590,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,yymsp[0].minor.yy403);} break; case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ -{ yymsp[-8].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy14,yymsp[-3].minor.yy454,0,0,yymsp[0].minor.yy122); } +{ yymsp[-8].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy402,yymsp[-3].minor.yy590,0,0,yymsp[0].minor.yy403); } break; case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */ -{ yymsp[-4].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } +{ yymsp[-4].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } break; case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ -{ yymsp[-7].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,0);} +{ yymsp[-7].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,0);} break; case 172: /* returning ::= RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy14);} +{sqlite3AddReturning(pParse,yymsp[0].minor.yy402);} break; case 175: /* idlist_opt ::= */ -{yymsp[1].minor.yy132 = 0;} +{yymsp[1].minor.yy204 = 0;} break; case 176: /* idlist_opt ::= LP idlist RP */ -{yymsp[-2].minor.yy132 = yymsp[-1].minor.yy132;} +{yymsp[-2].minor.yy204 = yymsp[-1].minor.yy204;} break; case 177: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy132 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy132,&yymsp[0].minor.yy0);} +{yymsp[-2].minor.yy204 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy204,&yymsp[0].minor.yy0);} break; case 178: /* idlist ::= nm */ -{yymsp[0].minor.yy132 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} +{yymsp[0].minor.yy204 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; case 179: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy454 = yymsp[-1].minor.yy454;} +{yymsp[-2].minor.yy590 = yymsp[-1].minor.yy590;} break; case 180: /* expr ::= ID|INDEXED|JOIN_KW */ -{yymsp[0].minor.yy454=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy590=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 181: /* expr ::= nm DOT nm */ { Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); - yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[-2].minor.yy454 = yylhsminor.yy454; + yymsp[-2].minor.yy590 = yylhsminor.yy590; break; case 182: /* expr ::= nm DOT nm DOT nm */ { @@ -178324,27 +178616,27 @@ static YYACTIONTYPE yy_reduce( if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, 0, temp1); } - yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); + yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } - yymsp[-4].minor.yy454 = yylhsminor.yy454; + yymsp[-4].minor.yy590 = yylhsminor.yy590; break; case 183: /* term ::= NULL|FLOAT|BLOB */ case 184: /* term ::= STRING */ yytestcase(yyruleno==184); -{yymsp[0].minor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 185: /* term ::= INTEGER */ { - yylhsminor.yy454 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); - if( yylhsminor.yy454 ) yylhsminor.yy454->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); + yylhsminor.yy590 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + if( yylhsminor.yy590 ) yylhsminor.yy590->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); } - yymsp[0].minor.yy454 = yylhsminor.yy454; + yymsp[0].minor.yy590 = yylhsminor.yy590; break; case 186: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; - yymsp[0].minor.yy454 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy454, n); + yymsp[0].minor.yy590 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy590, n); }else{ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers @@ -178353,80 +178645,80 @@ static YYACTIONTYPE yy_reduce( assert( t.n>=2 ); if( pParse->nested==0 ){ parserSyntaxError(pParse, &t); - yymsp[0].minor.yy454 = 0; + yymsp[0].minor.yy590 = 0; }else{ - yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); - if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable); + yymsp[0].minor.yy590 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy590 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy590->iTable); } } } break; case 187: /* expr ::= expr COLLATE ID|STRING */ { - yymsp[-2].minor.yy454 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy454, &yymsp[0].minor.yy0, 1); + yymsp[-2].minor.yy590 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy590, &yymsp[0].minor.yy0, 1); } break; case 188: /* expr ::= CAST LP expr AS typetoken RP */ { - yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); - sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy454, yymsp[-3].minor.yy454, 0); + yymsp[-5].minor.yy590 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy590, yymsp[-3].minor.yy590, 0); } break; case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ { - yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy144); + yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy502); } - yymsp[-4].minor.yy454 = yylhsminor.yy454; + yymsp[-4].minor.yy590 = yylhsminor.yy590; break; case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ { - yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy14, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy144); - sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-1].minor.yy14); + yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy402, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy502); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-1].minor.yy402); } - yymsp[-7].minor.yy454 = yylhsminor.yy454; + yymsp[-7].minor.yy590 = yylhsminor.yy590; break; case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ { - yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } - yymsp[-3].minor.yy454 = yylhsminor.yy454; + yymsp[-3].minor.yy590 = yylhsminor.yy590; break; case 192: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ { - yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy14, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy144); - sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); + yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy402, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy502); + sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); } - yymsp[-5].minor.yy454 = yylhsminor.yy454; + yymsp[-5].minor.yy590 = yylhsminor.yy590; break; case 193: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ { - yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy14, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy144); - sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); - sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-2].minor.yy14); + yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy402, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy502); + sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-2].minor.yy402); } - yymsp[-8].minor.yy454 = yylhsminor.yy454; + yymsp[-8].minor.yy590 = yylhsminor.yy590; break; case 194: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ { - yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); - sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); + yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); } - yymsp[-4].minor.yy454 = yylhsminor.yy454; + yymsp[-4].minor.yy590 = yylhsminor.yy590; break; case 195: /* term ::= CTIME_KW */ { - yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } - yymsp[0].minor.yy454 = yylhsminor.yy454; + yymsp[0].minor.yy590 = yylhsminor.yy590; break; case 196: /* expr ::= LP nexprlist COMMA expr RP */ { - ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454); - yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); - if( yymsp[-4].minor.yy454 ){ - yymsp[-4].minor.yy454->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590); + yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy590 ){ + yymsp[-4].minor.yy590->x.pList = pList; if( ALWAYS(pList->nExpr) ){ - yymsp[-4].minor.yy454->flags |= pList->a[0].pExpr->flags & EP_Propagate; + yymsp[-4].minor.yy590->flags |= pList->a[0].pExpr->flags & EP_Propagate; } }else{ sqlite3ExprListDelete(pParse->db, pList); @@ -178434,7 +178726,7 @@ static YYACTIONTYPE yy_reduce( } break; case 197: /* expr ::= expr AND expr */ -{yymsp[-2].minor.yy454=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} +{yymsp[-2].minor.yy590=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} break; case 198: /* expr ::= expr OR expr */ case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199); @@ -178443,7 +178735,7 @@ static YYACTIONTYPE yy_reduce( case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202); case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203); case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204); -{yymsp[-2].minor.yy454=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} +{yymsp[-2].minor.yy590=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} break; case 205: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} @@ -178453,11 +178745,11 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; yymsp[-1].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy454); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy454); - yymsp[-2].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); - if( bNot ) yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy454, 0); - if( yymsp[-2].minor.yy454 ) yymsp[-2].minor.yy454->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy590); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy590); + yymsp[-2].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy590, 0); + if( yymsp[-2].minor.yy590 ) yymsp[-2].minor.yy590->flags |= EP_InfixFunc; } break; case 207: /* expr ::= expr likeop expr ESCAPE expr */ @@ -178465,91 +178757,91 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; yymsp[-3].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy454); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454); - yymsp[-4].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); - if( bNot ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); - if( yymsp[-4].minor.yy454 ) yymsp[-4].minor.yy454->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy590); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590); + yymsp[-4].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); + if( yymsp[-4].minor.yy590 ) yymsp[-4].minor.yy590->flags |= EP_InfixFunc; } break; case 208: /* expr ::= expr ISNULL|NOTNULL */ -{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy454,0);} +{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy590,0);} break; case 209: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy454,0);} +{yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy590,0);} break; case 210: /* expr ::= expr IS expr */ { - yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy454,yymsp[0].minor.yy454); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-2].minor.yy454, TK_ISNULL); + yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy590,yymsp[0].minor.yy590); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-2].minor.yy590, TK_ISNULL); } break; case 211: /* expr ::= expr IS NOT expr */ { - yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy454,yymsp[0].minor.yy454); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-3].minor.yy454, TK_NOTNULL); + yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy590,yymsp[0].minor.yy590); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-3].minor.yy590, TK_NOTNULL); } break; case 212: /* expr ::= expr IS NOT DISTINCT FROM expr */ { - yymsp[-5].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy454,yymsp[0].minor.yy454); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-5].minor.yy454, TK_ISNULL); + yymsp[-5].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy590,yymsp[0].minor.yy590); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-5].minor.yy590, TK_ISNULL); } break; case 213: /* expr ::= expr IS DISTINCT FROM expr */ { - yymsp[-4].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy454,yymsp[0].minor.yy454); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-4].minor.yy454, TK_NOTNULL); + yymsp[-4].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy590,yymsp[0].minor.yy590); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-4].minor.yy590, TK_NOTNULL); } break; case 214: /* expr ::= NOT expr */ case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); -{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/} +{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy590, 0);/*A-overwrites-B*/} break; case 216: /* expr ::= PLUS|MINUS expr */ { - Expr *p = yymsp[0].minor.yy454; + Expr *p = yymsp[0].minor.yy590; u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS); assert( TK_UPLUS>TK_PLUS ); assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) ); if( p && p->op==TK_UPLUS ){ p->op = op; - yymsp[-1].minor.yy454 = p; + yymsp[-1].minor.yy590 = p; }else{ - yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0); + yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, op, p, 0); /*A-overwrites-B*/ } } break; case 217: /* expr ::= expr PTR expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454); - pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy454); - yylhsminor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy590); + pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy590); + yylhsminor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); } - yymsp[-2].minor.yy454 = yylhsminor.yy454; + yymsp[-2].minor.yy590 = yylhsminor.yy590; break; case 218: /* between_op ::= BETWEEN */ case 221: /* in_op ::= IN */ yytestcase(yyruleno==221); -{yymsp[0].minor.yy144 = 0;} +{yymsp[0].minor.yy502 = 0;} break; case 220: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454); - yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy454, 0); - if( yymsp[-4].minor.yy454 ){ - yymsp[-4].minor.yy454->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590); + yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy590, 0); + if( yymsp[-4].minor.yy590 ){ + yymsp[-4].minor.yy590->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); + if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); } break; case 223: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy14==0 ){ + if( yymsp[-1].minor.yy402==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -178558,110 +178850,110 @@ static YYACTIONTYPE yy_reduce( ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ - sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy454); - yymsp[-4].minor.yy454 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy144 ? "true" : "false"); - if( yymsp[-4].minor.yy454 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy454); - }else{ - Expr *pRHS = yymsp[-1].minor.yy14->a[0].pExpr; - if( yymsp[-1].minor.yy14->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy454->op!=TK_VECTOR ){ - yymsp[-1].minor.yy14->a[0].pExpr = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); + sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy590); + yymsp[-4].minor.yy590 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy502 ? "true" : "false"); + if( yymsp[-4].minor.yy590 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy590); + }else{ + Expr *pRHS = yymsp[-1].minor.yy402->a[0].pExpr; + if( yymsp[-1].minor.yy402->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy590->op!=TK_VECTOR ){ + yymsp[-1].minor.yy402->a[0].pExpr = 0; + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); - yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy454, pRHS); - }else if( yymsp[-1].minor.yy14->nExpr==1 && pRHS->op==TK_SELECT ){ - yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pRHS->x.pSelect); + yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy590, pRHS); + }else if( yymsp[-1].minor.yy402->nExpr==1 && pRHS->op==TK_SELECT ){ + yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pRHS->x.pSelect); pRHS->x.pSelect = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); - }else{ - yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); - if( yymsp[-4].minor.yy454==0 ){ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); - }else if( yymsp[-4].minor.yy454->pLeft->op==TK_VECTOR ){ - int nExpr = yymsp[-4].minor.yy454->pLeft->x.pList->nExpr; - Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy14); + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); + }else{ + yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); + if( yymsp[-4].minor.yy590==0 ){ + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); + }else if( yymsp[-4].minor.yy590->pLeft->op==TK_VECTOR ){ + int nExpr = yymsp[-4].minor.yy590->pLeft->x.pList->nExpr; + Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy402); if( pSelectRHS ){ parserDoubleLinkSelect(pParse, pSelectRHS); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelectRHS); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelectRHS); } }else{ - yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy14; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454); + yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy402; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590); } } - if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); + if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); } } break; case 224: /* expr ::= LP select RP */ { - yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); - sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy454, yymsp[-1].minor.yy555); + yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); + sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy590, yymsp[-1].minor.yy637); } break; case 225: /* expr ::= expr in_op LP select RP */ { - yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, yymsp[-1].minor.yy555); - if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); + yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, yymsp[-1].minor.yy637); + if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); } break; case 226: /* expr ::= expr in_op nm dbnm paren_exprlist */ { SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); - if( yymsp[0].minor.yy14 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy14); - yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelect); - if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); + if( yymsp[0].minor.yy402 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy402); + yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelect); + if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); } break; case 227: /* expr ::= EXISTS LP select RP */ { Expr *p; - p = yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); - sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy555); + p = yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy637); } break; case 228: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy454, 0); - if( yymsp[-4].minor.yy454 ){ - yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy454 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454) : yymsp[-2].minor.yy14; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454); + yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy590, 0); + if( yymsp[-4].minor.yy590 ){ + yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy590 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590) : yymsp[-2].minor.yy402; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14); - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy402); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590); } } break; case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy454); - yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[0].minor.yy454); + yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[-2].minor.yy590); + yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[0].minor.yy590); } break; case 230: /* case_exprlist ::= WHEN expr THEN expr */ { - yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); - yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, yymsp[0].minor.yy454); + yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); + yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy402, yymsp[0].minor.yy590); } break; case 235: /* nexprlist ::= nexprlist COMMA expr */ -{yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy454);} +{yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[0].minor.yy590);} break; case 236: /* nexprlist ::= expr */ -{yymsp[0].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy454); /*A-overwrites-Y*/} +{yymsp[0].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy590); /*A-overwrites-Y*/} break; case 238: /* paren_exprlist ::= LP exprlist RP */ case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243); -{yymsp[-2].minor.yy14 = yymsp[-1].minor.yy14;} +{yymsp[-2].minor.yy402 = yymsp[-1].minor.yy402;} break; case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, - sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy144, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy454, SQLITE_SO_ASC, yymsp[-8].minor.yy144, SQLITE_IDXTYPE_APPDEF); + sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy402, yymsp[-10].minor.yy502, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy590, SQLITE_SO_ASC, yymsp[-8].minor.yy502, SQLITE_IDXTYPE_APPDEF); if( IN_RENAME_OBJECT && pParse->pNewIndex ){ sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); } @@ -178669,29 +178961,29 @@ static YYACTIONTYPE yy_reduce( break; case 240: /* uniqueflag ::= UNIQUE */ case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282); -{yymsp[0].minor.yy144 = OE_Abort;} +{yymsp[0].minor.yy502 = OE_Abort;} break; case 241: /* uniqueflag ::= */ -{yymsp[1].minor.yy144 = OE_None;} +{yymsp[1].minor.yy502 = OE_None;} break; case 244: /* eidlist ::= eidlist COMMA nm collate sortorder */ { - yymsp[-4].minor.yy14 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); + yymsp[-4].minor.yy402 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); } break; case 245: /* eidlist ::= nm collate sortorder */ { - yymsp[-2].minor.yy14 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); /*A-overwrites-Y*/ + yymsp[-2].minor.yy402 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); /*A-overwrites-Y*/ } break; case 248: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy203, yymsp[-1].minor.yy144);} +{sqlite3DropIndex(pParse, yymsp[0].minor.yy563, yymsp[-1].minor.yy502);} break; case 249: /* cmd ::= VACUUM vinto */ -{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy454);} +{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy590);} break; case 250: /* cmd ::= VACUUM nm vinto */ -{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy454);} +{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy590);} break; case 253: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} @@ -178713,50 +179005,50 @@ static YYACTIONTYPE yy_reduce( Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy427, &all); + sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy319, &all); } break; case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy144, yymsp[-4].minor.yy286.a, yymsp[-4].minor.yy286.b, yymsp[-2].minor.yy203, yymsp[0].minor.yy454, yymsp[-10].minor.yy144, yymsp[-8].minor.yy144); + sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy502, yymsp[-4].minor.yy28.a, yymsp[-4].minor.yy28.b, yymsp[-2].minor.yy563, yymsp[0].minor.yy590, yymsp[-10].minor.yy502, yymsp[-8].minor.yy502); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; case 262: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/ } +{ yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/ } break; case 263: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy144 = TK_INSTEAD;} +{ yymsp[-1].minor.yy502 = TK_INSTEAD;} break; case 264: /* trigger_time ::= */ -{ yymsp[1].minor.yy144 = TK_BEFORE; } +{ yymsp[1].minor.yy502 = TK_BEFORE; } break; case 265: /* trigger_event ::= DELETE|INSERT */ case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266); -{yymsp[0].minor.yy286.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy286.b = 0;} +{yymsp[0].minor.yy28.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy28.b = 0;} break; case 267: /* trigger_event ::= UPDATE OF idlist */ -{yymsp[-2].minor.yy286.a = TK_UPDATE; yymsp[-2].minor.yy286.b = yymsp[0].minor.yy132;} +{yymsp[-2].minor.yy28.a = TK_UPDATE; yymsp[-2].minor.yy28.b = yymsp[0].minor.yy204;} break; case 268: /* when_clause ::= */ case 287: /* key_opt ::= */ yytestcase(yyruleno==287); -{ yymsp[1].minor.yy454 = 0; } +{ yymsp[1].minor.yy590 = 0; } break; case 269: /* when_clause ::= WHEN expr */ case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288); -{ yymsp[-1].minor.yy454 = yymsp[0].minor.yy454; } +{ yymsp[-1].minor.yy590 = yymsp[0].minor.yy590; } break; case 270: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy427!=0 ); - yymsp[-2].minor.yy427->pLast->pNext = yymsp[-1].minor.yy427; - yymsp[-2].minor.yy427->pLast = yymsp[-1].minor.yy427; + assert( yymsp[-2].minor.yy319!=0 ); + yymsp[-2].minor.yy319->pLast->pNext = yymsp[-1].minor.yy319; + yymsp[-2].minor.yy319->pLast = yymsp[-1].minor.yy319; } break; case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy427!=0 ); - yymsp[-1].minor.yy427->pLast = yymsp[-1].minor.yy427; + assert( yymsp[-1].minor.yy319!=0 ); + yymsp[-1].minor.yy319->pLast = yymsp[-1].minor.yy319; } break; case 272: /* trnm ::= nm DOT nm */ @@ -178782,58 +179074,58 @@ static YYACTIONTYPE yy_reduce( } break; case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -{yylhsminor.yy427 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy203, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454, yymsp[-7].minor.yy144, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy168);} - yymsp[-8].minor.yy427 = yylhsminor.yy427; +{yylhsminor.yy319 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy563, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590, yymsp[-7].minor.yy502, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy342);} + yymsp[-8].minor.yy319 = yylhsminor.yy319; break; case 276: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ { - yylhsminor.yy427 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy132,yymsp[-2].minor.yy555,yymsp[-6].minor.yy144,yymsp[-1].minor.yy122,yymsp[-7].minor.yy168,yymsp[0].minor.yy168);/*yylhsminor.yy427-overwrites-yymsp[-6].minor.yy144*/ + yylhsminor.yy319 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy204,yymsp[-2].minor.yy637,yymsp[-6].minor.yy502,yymsp[-1].minor.yy403,yymsp[-7].minor.yy342,yymsp[0].minor.yy342);/*yylhsminor.yy319-overwrites-yymsp[-6].minor.yy502*/ } - yymsp[-7].minor.yy427 = yylhsminor.yy427; + yymsp[-7].minor.yy319 = yylhsminor.yy319; break; case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -{yylhsminor.yy427 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy454, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy168);} - yymsp[-5].minor.yy427 = yylhsminor.yy427; +{yylhsminor.yy319 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy590, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy342);} + yymsp[-5].minor.yy319 = yylhsminor.yy319; break; case 278: /* trigger_cmd ::= scanpt select scanpt */ -{yylhsminor.yy427 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy555, yymsp[-2].minor.yy168, yymsp[0].minor.yy168); /*yylhsminor.yy427-overwrites-yymsp[-1].minor.yy555*/} - yymsp[-2].minor.yy427 = yylhsminor.yy427; +{yylhsminor.yy319 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy637, yymsp[-2].minor.yy342, yymsp[0].minor.yy342); /*yylhsminor.yy319-overwrites-yymsp[-1].minor.yy637*/} + yymsp[-2].minor.yy319 = yylhsminor.yy319; break; case 279: /* expr ::= RAISE LP IGNORE RP */ { - yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); - if( yymsp[-3].minor.yy454 ){ - yymsp[-3].minor.yy454->affExpr = OE_Ignore; + yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy590 ){ + yymsp[-3].minor.yy590->affExpr = OE_Ignore; } } break; case 280: /* expr ::= RAISE LP raisetype COMMA expr RP */ { - yymsp[-5].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy454, 0); - if( yymsp[-5].minor.yy454 ) { - yymsp[-5].minor.yy454->affExpr = (char)yymsp[-3].minor.yy144; + yymsp[-5].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy590, 0); + if( yymsp[-5].minor.yy590 ) { + yymsp[-5].minor.yy590->affExpr = (char)yymsp[-3].minor.yy502; } } break; case 281: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy144 = OE_Rollback;} +{yymsp[0].minor.yy502 = OE_Rollback;} break; case 283: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy144 = OE_Fail;} +{yymsp[0].minor.yy502 = OE_Fail;} break; case 284: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy203,yymsp[-1].minor.yy144); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy563,yymsp[-1].minor.yy502); } break; case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlite3Attach(pParse, yymsp[-3].minor.yy454, yymsp[-1].minor.yy454, yymsp[0].minor.yy454); + sqlite3Attach(pParse, yymsp[-3].minor.yy590, yymsp[-1].minor.yy590, yymsp[0].minor.yy590); } break; case 286: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy454); + sqlite3Detach(pParse, yymsp[0].minor.yy590); } break; case 289: /* cmd ::= REINDEX */ @@ -178850,7 +179142,7 @@ static YYACTIONTYPE yy_reduce( break; case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy203,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy563,&yymsp[0].minor.yy0); } break; case 294: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ @@ -178861,18 +179153,18 @@ static YYACTIONTYPE yy_reduce( break; case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ { - sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0); + sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy563, &yymsp[0].minor.yy0); } break; case 296: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy203); + sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy563); } break; case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { - sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy203, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy563, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; case 298: /* cmd ::= create_vtab */ @@ -178883,7 +179175,7 @@ static YYACTIONTYPE yy_reduce( break; case 300: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy144); + sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy502); } break; case 301: /* vtabarg ::= */ @@ -178896,20 +179188,20 @@ static YYACTIONTYPE yy_reduce( break; case 305: /* with ::= WITH wqlist */ case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306); -{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); } +{ sqlite3WithPush(pParse, yymsp[0].minor.yy125, 1); } break; case 307: /* wqas ::= AS */ -{yymsp[0].minor.yy462 = M10d_Any;} +{yymsp[0].minor.yy444 = M10d_Any;} break; case 308: /* wqas ::= AS MATERIALIZED */ -{yymsp[-1].minor.yy462 = M10d_Yes;} +{yymsp[-1].minor.yy444 = M10d_Yes;} break; case 309: /* wqas ::= AS NOT MATERIALIZED */ -{yymsp[-2].minor.yy462 = M10d_No;} +{yymsp[-2].minor.yy444 = M10d_No;} break; case 310: /* wqitem ::= withnm eidlist_opt wqas LP select RP */ { - yymsp[-5].minor.yy67 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy555, yymsp[-3].minor.yy462); /*A-overwrites-X*/ + yymsp[-5].minor.yy361 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy402, yymsp[-1].minor.yy637, yymsp[-3].minor.yy444); /*A-overwrites-X*/ } break; case 311: /* withnm ::= nm */ @@ -178917,160 +179209,160 @@ static YYACTIONTYPE yy_reduce( break; case 312: /* wqlist ::= wqitem */ { - yymsp[0].minor.yy59 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy67); /*A-overwrites-X*/ + yymsp[0].minor.yy125 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy361); /*A-overwrites-X*/ } break; case 313: /* wqlist ::= wqlist COMMA wqitem */ { - yymsp[-2].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy59, yymsp[0].minor.yy67); + yymsp[-2].minor.yy125 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy125, yymsp[0].minor.yy361); } break; case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - assert( yymsp[0].minor.yy211!=0 ); - sqlite3WindowChain(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy211); - yymsp[0].minor.yy211->pNextWin = yymsp[-2].minor.yy211; - yylhsminor.yy211 = yymsp[0].minor.yy211; + assert( yymsp[0].minor.yy483!=0 ); + sqlite3WindowChain(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy483); + yymsp[0].minor.yy483->pNextWin = yymsp[-2].minor.yy483; + yylhsminor.yy483 = yymsp[0].minor.yy483; } - yymsp[-2].minor.yy211 = yylhsminor.yy211; + yymsp[-2].minor.yy483 = yylhsminor.yy483; break; case 315: /* windowdefn ::= nm AS LP window RP */ { - if( ALWAYS(yymsp[-1].minor.yy211) ){ - yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); + if( ALWAYS(yymsp[-1].minor.yy483) ){ + yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); } - yylhsminor.yy211 = yymsp[-1].minor.yy211; + yylhsminor.yy483 = yymsp[-1].minor.yy483; } - yymsp[-4].minor.yy211 = yylhsminor.yy211; + yymsp[-4].minor.yy483 = yylhsminor.yy483; break; case 316: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { - yymsp[-4].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, 0); + yymsp[-4].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, 0); } break; case 317: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ { - yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, &yymsp[-5].minor.yy0); + yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, &yymsp[-5].minor.yy0); } - yymsp[-5].minor.yy211 = yylhsminor.yy211; + yymsp[-5].minor.yy483 = yylhsminor.yy483; break; case 318: /* window ::= ORDER BY sortlist frame_opt */ { - yymsp[-3].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, 0); + yymsp[-3].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, 0); } break; case 319: /* window ::= nm ORDER BY sortlist frame_opt */ { - yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0); + yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0); } - yymsp[-4].minor.yy211 = yylhsminor.yy211; + yymsp[-4].minor.yy483 = yylhsminor.yy483; break; case 320: /* window ::= nm frame_opt */ { - yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, 0, &yymsp[-1].minor.yy0); + yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, 0, &yymsp[-1].minor.yy0); } - yymsp[-1].minor.yy211 = yylhsminor.yy211; + yymsp[-1].minor.yy483 = yylhsminor.yy483; break; case 321: /* frame_opt ::= */ { - yymsp[1].minor.yy211 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); + yymsp[1].minor.yy483 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; case 322: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { - yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy144, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy462); + yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy502, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy444); } - yymsp[-2].minor.yy211 = yylhsminor.yy211; + yymsp[-2].minor.yy483 = yylhsminor.yy483; break; case 323: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { - yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy144, yymsp[-3].minor.yy509.eType, yymsp[-3].minor.yy509.pExpr, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, yymsp[0].minor.yy462); + yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy502, yymsp[-3].minor.yy205.eType, yymsp[-3].minor.yy205.pExpr, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, yymsp[0].minor.yy444); } - yymsp[-5].minor.yy211 = yylhsminor.yy211; + yymsp[-5].minor.yy483 = yylhsminor.yy483; break; case 325: /* frame_bound_s ::= frame_bound */ case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327); -{yylhsminor.yy509 = yymsp[0].minor.yy509;} - yymsp[0].minor.yy509 = yylhsminor.yy509; +{yylhsminor.yy205 = yymsp[0].minor.yy205;} + yymsp[0].minor.yy205 = yylhsminor.yy205; break; case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */ case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328); case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330); -{yylhsminor.yy509.eType = yymsp[-1].major; yylhsminor.yy509.pExpr = 0;} - yymsp[-1].minor.yy509 = yylhsminor.yy509; +{yylhsminor.yy205.eType = yymsp[-1].major; yylhsminor.yy205.pExpr = 0;} + yymsp[-1].minor.yy205 = yylhsminor.yy205; break; case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */ -{yylhsminor.yy509.eType = yymsp[0].major; yylhsminor.yy509.pExpr = yymsp[-1].minor.yy454;} - yymsp[-1].minor.yy509 = yylhsminor.yy509; +{yylhsminor.yy205.eType = yymsp[0].major; yylhsminor.yy205.pExpr = yymsp[-1].minor.yy590;} + yymsp[-1].minor.yy205 = yylhsminor.yy205; break; case 331: /* frame_exclude_opt ::= */ -{yymsp[1].minor.yy462 = 0;} +{yymsp[1].minor.yy444 = 0;} break; case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ -{yymsp[-1].minor.yy462 = yymsp[0].minor.yy462;} +{yymsp[-1].minor.yy444 = yymsp[0].minor.yy444;} break; case 333: /* frame_exclude ::= NO OTHERS */ case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334); -{yymsp[-1].minor.yy462 = yymsp[-1].major; /*A-overwrites-X*/} +{yymsp[-1].minor.yy444 = yymsp[-1].major; /*A-overwrites-X*/} break; case 335: /* frame_exclude ::= GROUP|TIES */ -{yymsp[0].minor.yy462 = yymsp[0].major; /*A-overwrites-X*/} +{yymsp[0].minor.yy444 = yymsp[0].major; /*A-overwrites-X*/} break; case 336: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy211 = yymsp[0].minor.yy211; } +{ yymsp[-1].minor.yy483 = yymsp[0].minor.yy483; } break; case 337: /* filter_over ::= filter_clause over_clause */ { - if( yymsp[0].minor.yy211 ){ - yymsp[0].minor.yy211->pFilter = yymsp[-1].minor.yy454; + if( yymsp[0].minor.yy483 ){ + yymsp[0].minor.yy483->pFilter = yymsp[-1].minor.yy590; }else{ - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590); } - yylhsminor.yy211 = yymsp[0].minor.yy211; + yylhsminor.yy483 = yymsp[0].minor.yy483; } - yymsp[-1].minor.yy211 = yylhsminor.yy211; + yymsp[-1].minor.yy483 = yylhsminor.yy483; break; case 338: /* filter_over ::= over_clause */ { - yylhsminor.yy211 = yymsp[0].minor.yy211; + yylhsminor.yy483 = yymsp[0].minor.yy483; } - yymsp[0].minor.yy211 = yylhsminor.yy211; + yymsp[0].minor.yy483 = yylhsminor.yy483; break; case 339: /* filter_over ::= filter_clause */ { - yylhsminor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yylhsminor.yy211 ){ - yylhsminor.yy211->eFrmType = TK_FILTER; - yylhsminor.yy211->pFilter = yymsp[0].minor.yy454; + yylhsminor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy483 ){ + yylhsminor.yy483->eFrmType = TK_FILTER; + yylhsminor.yy483->pFilter = yymsp[0].minor.yy590; }else{ - sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy454); + sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy590); } } - yymsp[0].minor.yy211 = yylhsminor.yy211; + yymsp[0].minor.yy483 = yylhsminor.yy483; break; case 340: /* over_clause ::= OVER LP window RP */ { - yymsp[-3].minor.yy211 = yymsp[-1].minor.yy211; - assert( yymsp[-3].minor.yy211!=0 ); + yymsp[-3].minor.yy483 = yymsp[-1].minor.yy483; + assert( yymsp[-3].minor.yy483!=0 ); } break; case 341: /* over_clause ::= OVER nm */ { - yymsp[-1].minor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yymsp[-1].minor.yy211 ){ - yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + yymsp[-1].minor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yymsp[-1].minor.yy483 ){ + yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); } } break; case 342: /* filter_clause ::= FILTER LP WHERE expr RP */ -{ yymsp[-4].minor.yy454 = yymsp[-1].minor.yy454; } +{ yymsp[-4].minor.yy590 = yymsp[-1].minor.yy590; } break; case 343: /* term ::= QNUMBER */ { - yylhsminor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); - sqlite3DequoteNumber(pParse, yylhsminor.yy454); + yylhsminor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); + sqlite3DequoteNumber(pParse, yylhsminor.yy590); } - yymsp[0].minor.yy454 = yylhsminor.yy454; + yymsp[0].minor.yy590 = yylhsminor.yy590; break; default: /* (344) input ::= cmdlist */ yytestcase(yyruleno==344); @@ -180251,7 +180543,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ case CC_MINUS: { if( z[1]=='-' ){ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} - *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ + *tokenType = TK_COMMENT; return i; }else if( z[1]=='>' ){ *tokenType = TK_PTR; @@ -180287,7 +180579,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ } for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} if( c ) i++; - *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ + *tokenType = TK_COMMENT; return i; } case CC_PERCENT: { @@ -180616,12 +180908,12 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ if( tokenType>=TK_WINDOW ){ assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW - || tokenType==TK_QNUMBER + || tokenType==TK_QNUMBER || tokenType==TK_COMMENT ); #else if( tokenType>=TK_SPACE ){ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL - || tokenType==TK_QNUMBER + || tokenType==TK_QNUMBER || tokenType==TK_COMMENT ); #endif /* SQLITE_OMIT_WINDOWFUNC */ if( AtomicLoad(&db->u1.isInterrupted) ){ @@ -180655,6 +180947,9 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ assert( n==6 ); tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); #endif /* SQLITE_OMIT_WINDOWFUNC */ + }else if( tokenType==TK_COMMENT && (db->flags & SQLITE_Comments)!=0 ){ + zSql += n; + continue; }else if( tokenType!=TK_QNUMBER ){ Token x; x.z = zSql; @@ -180761,6 +181056,7 @@ SQLITE_PRIVATE char *sqlite3Normalize( n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType); if( NEVER(n<=0) ) break; switch( tokenType ){ + case TK_COMMENT: case TK_SPACE: { break; } @@ -182202,7 +182498,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ default: { static const struct { int op; /* The opcode */ - u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ + u64 mask; /* Mask of the bit in sqlite3.flags to set/clear */ } aFlagOp[] = { { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, @@ -182223,6 +182519,9 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ { SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema }, { SQLITE_DBCONFIG_STMT_SCANSTATUS, SQLITE_StmtScanStatus }, { SQLITE_DBCONFIG_REVERSE_SCANORDER, SQLITE_ReverseOrder }, + { SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, SQLITE_AttachCreate }, + { SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, SQLITE_AttachWrite }, + { SQLITE_DBCONFIG_ENABLE_COMMENTS, SQLITE_Comments }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ @@ -184564,6 +184863,9 @@ static int openDatabase( | SQLITE_EnableTrigger | SQLITE_EnableView | SQLITE_CacheSpill + | SQLITE_AttachCreate + | SQLITE_AttachWrite + | SQLITE_Comments #if !defined(SQLITE_TRUSTED_SCHEMA) || SQLITE_TRUSTED_SCHEMA+0!=0 | SQLITE_TrustedSchema #endif @@ -187869,6 +188171,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext( SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **); SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *); SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); +SQLITE_PRIVATE int sqlite3Fts3MsrCancel(Fts3Cursor*, Fts3Expr*); /* fts3_tokenize_vtab.c */ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *, void(*xDestroy)(void*)); @@ -193387,6 +193690,24 @@ static void fts3EvalRestart( } } +/* +** Expression node pExpr is an MSR phrase. This function restarts pExpr +** so that it is a regular phrase query, not an MSR. SQLITE_OK is returned +** if successful, or an SQLite error code otherwise. +*/ +SQLITE_PRIVATE int sqlite3Fts3MsrCancel(Fts3Cursor *pCsr, Fts3Expr *pExpr){ + int rc = SQLITE_OK; + if( pExpr->bEof==0 ){ + i64 iDocid = pExpr->iDocid; + fts3EvalRestart(pCsr, pExpr, &rc); + while( rc==SQLITE_OK && pExpr->iDocid!=iDocid ){ + fts3EvalNextRow(pCsr, pExpr, &rc); + if( pExpr->bEof ) rc = FTS_CORRUPT_VTAB; + } + } + return rc; +} + /* ** After allocating the Fts3Expr.aMI[] array for each phrase in the ** expression rooted at pExpr, the cursor iterates through all rows matched @@ -205344,6 +205665,22 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ return rc; } +/* +** If expression pExpr is a phrase expression that uses an MSR query, +** restart it as a regular, non-incremental query. Return SQLITE_OK +** if successful, or an SQLite error code otherwise. +*/ +static int fts3ExprRestartIfCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ + TermOffsetCtx *p = (TermOffsetCtx*)ctx; + int rc = SQLITE_OK; + UNUSED_PARAMETER(iPhrase); + if( pExpr->pPhrase && pExpr->pPhrase->bIncr ){ + rc = sqlite3Fts3MsrCancel(p->pCsr, pExpr); + pExpr->pPhrase->bIncr = 0; + } + return rc; +} + /* ** Implementation of offsets() function. */ @@ -205380,6 +205717,12 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( sCtx.iDocid = pCsr->iPrevId; sCtx.pCsr = pCsr; + /* If a query restart will be required, do it here, rather than later of + ** after pointers to poslist buffers that may be invalidated by a restart + ** have been saved. */ + rc = sqlite3Fts3ExprIterate(pCsr->pExpr, fts3ExprRestartIfCb, (void*)&sCtx); + if( rc!=SQLITE_OK ) goto offsets_out; + /* Loop through the table columns, appending offset information to ** string-buffer res for each column. */ @@ -226360,6 +226703,7 @@ static int dbpageUpdate( return rc; update_fail: + pTab->pgnoTrunc = 0; sqlite3_free(pVtab->zErrMsg); pVtab->zErrMsg = sqlite3_mprintf("%s", zErr); return SQLITE_ERROR; @@ -226577,11 +226921,13 @@ struct sqlite3_changeset_iter { struct SessionTable { SessionTable *pNext; char *zName; /* Local name of table */ - int nCol; /* Number of columns in table zName */ + int nCol; /* Number of non-hidden columns */ + int nTotalCol; /* Number of columns including hidden */ int bStat1; /* True if this is sqlite_stat1 */ int bRowid; /* True if this table uses rowid for PK */ const char **azCol; /* Column names */ const char **azDflt; /* Default value expressions */ + int *aiIdx; /* Index to pass to xNew/xOld */ u8 *abPK; /* Array of primary key flags */ int nEntry; /* Total number of entries in hash table */ int nChange; /* Size of apChange[] array */ @@ -226984,22 +227330,22 @@ static int sessionPreupdateHash( unsigned int h = 0; /* Hash value to return */ int i; /* Used to iterate through columns */ + assert( pTab->nTotalCol==pSession->hook.xCount(pSession->hook.pCtx) ); if( pTab->bRowid ){ - assert( pTab->nCol-1==pSession->hook.xCount(pSession->hook.pCtx) ); h = sessionHashAppendI64(h, iRowid); }else{ assert( *pbNullPK==0 ); - assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) ); for(i=0; inCol; i++){ if( pTab->abPK[i] ){ int rc; int eType; sqlite3_value *pVal; + int iIdx = pTab->aiIdx[i]; if( bNew ){ - rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal); + rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal); }else{ - rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal); + rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal); } if( rc!=SQLITE_OK ) return rc; @@ -227336,6 +227682,7 @@ static int sessionPreupdateEqual( sqlite3_value *pVal; /* Value returned by preupdate_new/old */ int rc; /* Error code from preupdate_new/old */ int eType = *a++; /* Type of value from change record */ + int iIdx = pTab->aiIdx[iCol]; /* The following calls to preupdate_new() and preupdate_old() can not ** fail. This is because they cache their return values, and by the @@ -227344,10 +227691,10 @@ static int sessionPreupdateEqual( ** this (that the method has already been called). */ if( op==SQLITE_INSERT ){ /* assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew ); */ - rc = pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal); + rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal); }else{ /* assert( db->pPreUpdate->pUnpacked ); */ - rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal); + rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal); } assert( rc==SQLITE_OK ); (void)rc; /* Suppress warning about unused variable */ @@ -227472,9 +227819,11 @@ static int sessionTableInfo( const char *zDb, /* Name of attached database (e.g. "main") */ const char *zThis, /* Table name */ int *pnCol, /* OUT: number of columns */ + int *pnTotalCol, /* OUT: number of hidden columns */ const char **pzTab, /* OUT: Copy of zThis */ const char ***pazCol, /* OUT: Array of column names for table */ const char ***pazDflt, /* OUT: Array of default value expressions */ + int **paiIdx, /* OUT: Array of xNew/xOld indexes */ u8 **pabPK, /* OUT: Array of booleans - true for PK col */ int *pbRowid /* OUT: True if only PK is a rowid */ ){ @@ -227489,6 +227838,7 @@ static int sessionTableInfo( char **azCol = 0; char **azDflt = 0; u8 *abPK = 0; + int *aiIdx = 0; int bRowid = 0; /* Set to true to use rowid as PK */ assert( pazCol && pabPK ); @@ -227496,6 +227846,8 @@ static int sessionTableInfo( *pazCol = 0; *pabPK = 0; *pnCol = 0; + if( pnTotalCol ) *pnTotalCol = 0; + if( paiIdx ) *paiIdx = 0; if( pzTab ) *pzTab = 0; if( pazDflt ) *pazDflt = 0; @@ -227505,9 +227857,9 @@ static int sessionTableInfo( if( rc==SQLITE_OK ){ /* For sqlite_stat1, pretend that (tbl,idx) is the PRIMARY KEY. */ zPragma = sqlite3_mprintf( - "SELECT 0, 'tbl', '', 0, '', 1 UNION ALL " - "SELECT 1, 'idx', '', 0, '', 2 UNION ALL " - "SELECT 2, 'stat', '', 0, '', 0" + "SELECT 0, 'tbl', '', 0, '', 1, 0 UNION ALL " + "SELECT 1, 'idx', '', 0, '', 2, 0 UNION ALL " + "SELECT 2, 'stat', '', 0, '', 0, 0" ); }else if( rc==SQLITE_ERROR ){ zPragma = sqlite3_mprintf(""); @@ -227515,7 +227867,7 @@ static int sessionTableInfo( return rc; } }else{ - zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis); + zPragma = sqlite3_mprintf("PRAGMA '%q'.table_xinfo('%q')", zDb, zThis); } if( !zPragma ){ return SQLITE_NOMEM; @@ -227532,7 +227884,9 @@ static int sessionTableInfo( while( SQLITE_ROW==sqlite3_step(pStmt) ){ nByte += sqlite3_column_bytes(pStmt, 1); /* name */ nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */ - nDbCol++; + if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */ + nDbCol++; + } if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */ } if( nDbCol==0 ) bRowid = 0; @@ -227541,7 +227895,7 @@ static int sessionTableInfo( rc = sqlite3_reset(pStmt); if( rc==SQLITE_OK ){ - nByte += nDbCol * (sizeof(const char *)*2 + sizeof(u8) + 1 + 1); + nByte += nDbCol * (sizeof(const char *)*2 +sizeof(int)+sizeof(u8) + 1 + 1); pAlloc = sessionMalloc64(pSession, nByte); if( pAlloc==0 ){ rc = SQLITE_NOMEM; @@ -227552,8 +227906,8 @@ static int sessionTableInfo( if( rc==SQLITE_OK ){ azCol = (char **)pAlloc; azDflt = (char**)&azCol[nDbCol]; - pAlloc = (u8 *)&azDflt[nDbCol]; - abPK = (u8 *)pAlloc; + aiIdx = (int*)&azDflt[nDbCol]; + abPK = (u8 *)&aiIdx[nDbCol]; pAlloc = &abPK[nDbCol]; if( pzTab ){ memcpy(pAlloc, zThis, nThis+1); @@ -227568,27 +227922,32 @@ static int sessionTableInfo( azCol[i] = (char*)pAlloc; pAlloc += nName+1; abPK[i] = 1; + aiIdx[i] = -1; i++; } while( SQLITE_ROW==sqlite3_step(pStmt) ){ - int nName = sqlite3_column_bytes(pStmt, 1); - int nDflt = sqlite3_column_bytes(pStmt, 4); - const unsigned char *zName = sqlite3_column_text(pStmt, 1); - const unsigned char *zDflt = sqlite3_column_text(pStmt, 4); - - if( zName==0 ) break; - memcpy(pAlloc, zName, nName+1); - azCol[i] = (char *)pAlloc; - pAlloc += nName+1; - if( zDflt ){ - memcpy(pAlloc, zDflt, nDflt+1); - azDflt[i] = (char *)pAlloc; - pAlloc += nDflt+1; - }else{ - azDflt[i] = 0; + if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */ + int nName = sqlite3_column_bytes(pStmt, 1); + int nDflt = sqlite3_column_bytes(pStmt, 4); + const unsigned char *zName = sqlite3_column_text(pStmt, 1); + const unsigned char *zDflt = sqlite3_column_text(pStmt, 4); + + if( zName==0 ) break; + memcpy(pAlloc, zName, nName+1); + azCol[i] = (char *)pAlloc; + pAlloc += nName+1; + if( zDflt ){ + memcpy(pAlloc, zDflt, nDflt+1); + azDflt[i] = (char *)pAlloc; + pAlloc += nDflt+1; + }else{ + azDflt[i] = 0; + } + abPK[i] = sqlite3_column_int(pStmt, 5); + aiIdx[i] = sqlite3_column_int(pStmt, 0); + i++; } - abPK[i] = sqlite3_column_int(pStmt, 5); - i++; + if( pnTotalCol ) (*pnTotalCol)++; } rc = sqlite3_reset(pStmt); } @@ -227601,6 +227960,7 @@ static int sessionTableInfo( if( pazDflt ) *pazDflt = (const char**)azDflt; *pabPK = abPK; *pnCol = nDbCol; + if( paiIdx ) *paiIdx = aiIdx; }else{ sessionFree(pSession, azCol); } @@ -227632,7 +227992,8 @@ static int sessionInitTable( u8 *abPK; assert( pTab->azCol==0 || pTab->abPK==0 ); rc = sessionTableInfo(pSession, db, zDb, - pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->azDflt, &abPK, + pTab->zName, &pTab->nCol, &pTab->nTotalCol, 0, &pTab->azCol, + &pTab->azDflt, &pTab->aiIdx, &abPK, ((pSession==0 || pSession->bImplicitPK) ? &pTab->bRowid : 0) ); if( rc==SQLITE_OK ){ @@ -227667,15 +228028,17 @@ static int sessionInitTable( */ static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){ int nCol = 0; + int nTotalCol = 0; const char **azCol = 0; const char **azDflt = 0; + int *aiIdx = 0; u8 *abPK = 0; int bRowid = 0; assert( pSession->rc==SQLITE_OK ); pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, - pTab->zName, &nCol, 0, &azCol, &azDflt, &abPK, + pTab->zName, &nCol, &nTotalCol, 0, &azCol, &azDflt, &aiIdx, &abPK, (pSession->bImplicitPK ? &bRowid : 0) ); if( pSession->rc==SQLITE_OK ){ @@ -227698,8 +228061,10 @@ static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){ const char **a = pTab->azCol; pTab->azCol = azCol; pTab->nCol = nCol; + pTab->nTotalCol = nTotalCol; pTab->azDflt = azDflt; pTab->abPK = abPK; + pTab->aiIdx = aiIdx; azCol = a; } if( pSession->bEnableSize ){ @@ -228017,7 +228382,7 @@ static int sessionUpdateMaxSize( int ii; for(ii=0; iinCol; ii++){ sqlite3_value *p = 0; - pSession->hook.xNew(pSession->hook.pCtx, ii, &p); + pSession->hook.xNew(pSession->hook.pCtx, pTab->aiIdx[ii], &p); sessionSerializeValue(0, p, &nNew); } } @@ -228037,8 +228402,9 @@ static int sessionUpdateMaxSize( int bChanged = 1; int nOld = 0; int eType; + int iIdx = pTab->aiIdx[ii]; sqlite3_value *p = 0; - pSession->hook.xNew(pSession->hook.pCtx, ii-pTab->bRowid, &p); + pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p); if( p==0 ){ return SQLITE_NOMEM; } @@ -228135,11 +228501,11 @@ static void sessionPreupdateOneChange( /* Check the number of columns in this xPreUpdate call matches the ** number of columns in the table. */ nExpect = pSession->hook.xCount(pSession->hook.pCtx); - if( (pTab->nCol-pTab->bRowid)nTotalColnCol-pTab->bRowid)!=nExpect ){ + if( pTab->nTotalCol!=nExpect ){ pSession->rc = SQLITE_SCHEMA; return; } @@ -228196,14 +228562,15 @@ static void sessionPreupdateOneChange( /* Figure out how large an allocation is required */ nByte = sizeof(SessionChange); - for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ + for(i=pTab->bRowid; inCol; i++){ + int iIdx = pTab->aiIdx[i]; sqlite3_value *p = 0; if( op!=SQLITE_INSERT ){ /* This may fail if the column has a non-NULL default and was added ** using ALTER TABLE ADD COLUMN after this record was created. */ - rc = pSession->hook.xOld(pSession->hook.pCtx, i, &p); + rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p); }else if( pTab->abPK[i] ){ - TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p); + TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx,iIdx,&p); assert( trc==SQLITE_OK ); } @@ -228238,12 +228605,13 @@ static void sessionPreupdateOneChange( sessionPutI64(&pC->aRecord[1], iRowid); nByte = 9; } - for(i=0; i<(pTab->nCol-pTab->bRowid); i++){ + for(i=pTab->bRowid; inCol; i++){ sqlite3_value *p = 0; + int iIdx = pTab->aiIdx[i]; if( op!=SQLITE_INSERT ){ - pSession->hook.xOld(pSession->hook.pCtx, i, &p); + pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p); }else if( pTab->abPK[i] ){ - pSession->hook.xNew(pSession->hook.pCtx, i, &p); + pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p); } sessionSerializeValue(&pC->aRecord[nByte], p, &nByte); } @@ -228645,7 +229013,8 @@ SQLITE_API int sqlite3session_diff( int bRowid = 0; u8 *abPK; const char **azCol = 0; - rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, 0, &abPK, + rc = sessionTableInfo(0, db, zFrom, zTbl, + &nCol, 0, 0, &azCol, 0, 0, &abPK, pSession->bImplicitPK ? &bRowid : 0 ); if( rc==SQLITE_OK ){ @@ -228969,9 +229338,11 @@ static void sessionAppendIdent( char *zOut = (char *)&p->aBuf[p->nBuf]; const char *zIn = zStr; *zOut++ = '"'; - while( *zIn ){ - if( *zIn=='"' ) *zOut++ = '"'; - *zOut++ = *(zIn++); + if( zIn!=0 ){ + while( *zIn ){ + if( *zIn=='"' ) *zOut++ = '"'; + *zOut++ = *(zIn++); + } } *zOut++ = '"'; p->nBuf = (int)((u8 *)zOut - p->aBuf); @@ -229222,10 +229593,10 @@ static int sessionSelectStmt( int rc = SQLITE_OK; char *zSql = 0; const char *zSep = ""; - const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*"; int nSql = -1; int i; + SessionBuffer cols = {0, 0, 0}; SessionBuffer nooptest = {0, 0, 0}; SessionBuffer pkfield = {0, 0, 0}; SessionBuffer pkvar = {0, 0, 0}; @@ -229238,9 +229609,16 @@ static int sessionSelectStmt( sessionAppendStr(&pkvar, "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc ); - zCols = "tbl, ?2, stat"; + sessionAppendStr(&cols, "tbl, ?2, stat", &rc); }else{ + #if 0 + if( bRowid ){ + sessionAppendStr(&cols, SESSIONS_ROWID, &rc); + } + #endif for(i=0; ipReader ){ + int rc; sqlite3_blob *pReader = p->pReader; p->pReader = 0; - sqlite3_blob_close(pReader); + rc = sqlite3_blob_close(pReader); + if( p->rc==SQLITE_OK ) p->rc = rc; } } @@ -243262,7 +243649,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ assert( p->pReader==0 ); p->pReader = pBlob; if( rc!=SQLITE_OK ){ - sqlite3Fts5IndexCloseReader(p); + fts5IndexCloseReader(p); } if( rc==SQLITE_ABORT ) rc = SQLITE_OK; } @@ -247464,6 +247851,14 @@ static int fts5IndexReturn(Fts5Index *p){ return rc; } +/* +** Close the read-only blob handle, if it is open. +*/ +static void sqlite3Fts5IndexCloseReader(Fts5Index *p){ + fts5IndexCloseReader(p); + fts5IndexReturn(p); +} + typedef struct Fts5FlushCtx Fts5FlushCtx; struct Fts5FlushCtx { Fts5Index *pIdx; @@ -247921,8 +248316,11 @@ static void fts5DoSecureDelete( ** This is called as part of flushing a delete to disk in 'secure-delete' ** mode. It edits the segments within the database described by argument ** pStruct to remove the entries for term zTerm, rowid iRowid. +** +** Return SQLITE_OK if successful, or an SQLite error code if an error +** has occurred. Any error code is also stored in the Fts5Index handle. */ -static void fts5FlushSecureDelete( +static int fts5FlushSecureDelete( Fts5Index *p, Fts5Structure *pStruct, const char *zTerm, @@ -247967,6 +248365,7 @@ static void fts5FlushSecureDelete( } fts5MultiIterFree(pIter); + return p->rc; } @@ -248050,8 +248449,9 @@ static void fts5FlushOneHash(Fts5Index *p){ ** using fts5FlushSecureDelete(). */ if( bSecureDelete ){ if( eDetail==FTS5_DETAIL_NONE ){ - if( iOffrc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ iOff++; continue; @@ -249185,7 +249586,7 @@ static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ static int sqlite3Fts5IndexSync(Fts5Index *p){ assert( p->rc==SQLITE_OK ); fts5IndexFlush(p); - sqlite3Fts5IndexCloseReader(p); + fts5IndexCloseReader(p); return fts5IndexReturn(p); } @@ -249196,11 +249597,10 @@ static int sqlite3Fts5IndexSync(Fts5Index *p){ ** records must be invalidated. */ static int sqlite3Fts5IndexRollback(Fts5Index *p){ - sqlite3Fts5IndexCloseReader(p); + fts5IndexCloseReader(p); fts5IndexDiscardData(p); fts5StructureInvalidate(p); - /* assert( p->rc==SQLITE_OK ); */ - return SQLITE_OK; + return fts5IndexReturn(p); } /* @@ -249401,6 +249801,16 @@ static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ pSeg->pLeaf = 0; } +static void fts5IterClose(Fts5IndexIter *pIndexIter){ + if( pIndexIter ){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + Fts5Index *pIndex = pIter->pIndex; + fts5TokendataIterDelete(pIter->pTokenDataIter); + fts5MultiIterFree(pIter); + fts5IndexCloseReader(pIndex); + } +} + /* ** This function appends iterator pAppend to Fts5TokenDataIter pIn and ** returns the result. @@ -249428,7 +249838,7 @@ static Fts5TokenDataIter *fts5AppendTokendataIter( } } if( p->rc ){ - sqlite3Fts5IterClose((Fts5IndexIter*)pAppend); + fts5IterClose((Fts5IndexIter*)pAppend); }else{ pRet->apIter[pRet->nIter++] = pAppend; } @@ -249641,7 +250051,7 @@ static Fts5Iter *fts5SetupTokendataIter( fts5BufferSet(&p->rc, &bSeek, nToken, pToken); } if( p->rc ){ - sqlite3Fts5IterClose((Fts5IndexIter*)pNew); + fts5IterClose((Fts5IndexIter*)pNew); break; } @@ -249706,7 +250116,7 @@ static Fts5Iter *fts5SetupTokendataIter( ** not point to any terms that match the query. So delete it and break ** out of the loop - all required iterators have been collected. */ if( pSmall==0 ){ - sqlite3Fts5IterClose((Fts5IndexIter*)pNew); + fts5IterClose((Fts5IndexIter*)pNew); break; } @@ -249835,9 +250245,9 @@ static int sqlite3Fts5IndexQuery( } if( p->rc ){ - sqlite3Fts5IterClose((Fts5IndexIter*)pRet); + fts5IterClose((Fts5IndexIter*)pRet); pRet = 0; - sqlite3Fts5IndexCloseReader(p); + fts5IndexCloseReader(p); } *ppIter = (Fts5IndexIter*)pRet; @@ -250087,11 +250497,9 @@ static int sqlite3Fts5IndexIterWriteTokendata( */ static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){ if( pIndexIter ){ - Fts5Iter *pIter = (Fts5Iter*)pIndexIter; - Fts5Index *pIndex = pIter->pIndex; - fts5TokendataIterDelete(pIter->pTokenDataIter); - fts5MultiIterFree(pIter); - sqlite3Fts5IndexCloseReader(pIndex); + Fts5Index *pIndex = ((Fts5Iter*)pIndexIter)->pIndex; + fts5IterClose(pIndexIter); + fts5IndexReturn(pIndex); } } @@ -250621,7 +251029,7 @@ static int fts5QueryCksum( rc = sqlite3Fts5IterNext(pIter); } } - sqlite3Fts5IterClose(pIter); + fts5IterClose(pIter); *pCksum = cksum; return rc; @@ -255465,7 +255873,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2025-02-06 11:55:18 4a7dd425dc2a0e5082a9049c9b4a9d4f199a71583d014c24b4cfe276c5a77cde", -1, SQLITE_TRANSIENT); } /* diff --git a/src/bun.js/bindings/sqlite/sqlite3_local.h b/src/bun.js/bindings/sqlite/sqlite3_local.h index 2c5b28b69ab55c..0a0e72ab39c604 100644 --- a/src/bun.js/bindings/sqlite/sqlite3_local.h +++ b/src/bun.js/bindings/sqlite/sqlite3_local.h @@ -147,9 +147,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.48.0" -#define SQLITE_VERSION_NUMBER 3048000 -#define SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010" +#define SQLITE_VERSION "3.49.0" +#define SQLITE_VERSION_NUMBER 3049000 +#define SQLITE_SOURCE_ID "2025-02-06 11:55:18 4a7dd425dc2a0e5082a9049c9b4a9d4f199a71583d014c24b4cfe276c5a77cde" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -2212,7 +2212,15 @@ struct sqlite3_mem_methods { ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that -** can be passed as the second argument to the [sqlite3_db_config()] interface. +** can be passed as the second parameter to the [sqlite3_db_config()] interface. +** +** The [sqlite3_db_config()] interface is a var-args functions. It takes a +** variable number of parameters, though always at least two. The number of +** parameters passed into sqlite3_db_config() depends on which of these +** constants is given as the second parameter. This documentation page +** refers to parameters beyond the second as "arguments". Thus, when this +** page says "the N-th argument" it means "the N-th parameter past the +** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()". ** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications @@ -2224,8 +2232,14 @@ struct sqlite3_mem_methods { **

** [[SQLITE_DBCONFIG_LOOKASIDE]] **
SQLITE_DBCONFIG_LOOKASIDE
-**
^This option takes three additional arguments that determine the -** [lookaside memory allocator] configuration for the [database connection]. +**
The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the +** configuration of the lookaside memory allocator within a database +** connection. +** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are not +** in the [DBCONFIG arguments|usual format]. +** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two, +** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE +** should have a total of five parameters. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a ** pointer to a memory buffer to use for lookaside memory. ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb @@ -2248,7 +2262,8 @@ struct sqlite3_mem_methods { ** [[SQLITE_DBCONFIG_ENABLE_FKEY]] **
SQLITE_DBCONFIG_ENABLE_FKEY
**
^This option is used to enable or disable the enforcement of -** [foreign key constraints]. There should be two additional arguments. +** [foreign key constraints]. This is the same setting that is +** enabled or disabled by the [PRAGMA foreign_keys] statement. ** The first argument is an integer which is 0 to disable FK enforcement, ** positive to enable FK enforcement or negative to leave FK enforcement ** unchanged. The second parameter is a pointer to an integer into which @@ -2270,13 +2285,13 @@ struct sqlite3_mem_methods { **

Originally this option disabled all triggers. ^(However, since ** SQLite version 3.35.0, TEMP triggers are still allowed even if ** this option is off. So, in other words, this option now only disables -** triggers in the main database schema or in the schemas of ATTACH-ed +** triggers in the main database schema or in the schemas of [ATTACH]-ed ** databases.)^

** ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] **
SQLITE_DBCONFIG_ENABLE_VIEW
**
^This option is used to enable or disable [CREATE VIEW | views]. -** There should be two additional arguments. +** There must be two additional arguments. ** The first argument is an integer which is 0 to disable views, ** positive to enable views or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which @@ -2295,7 +2310,7 @@ struct sqlite3_mem_methods { **
^This option is used to enable or disable the ** [fts3_tokenizer()] function which is part of the ** [FTS3] full-text search engine extension. -** There should be two additional arguments. +** There must be two additional arguments. ** The first argument is an integer which is 0 to disable fts3_tokenizer() or ** positive to enable fts3_tokenizer() or negative to leave the setting ** unchanged. @@ -2310,7 +2325,7 @@ struct sqlite3_mem_methods { ** interface independently of the [load_extension()] SQL function. ** The [sqlite3_enable_load_extension()] API enables or disables both the ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. -** There should be two additional arguments. +** There must be two additional arguments. ** When the first argument to this interface is 1, then only the C-API is ** enabled and the SQL function remains disabled. If the first argument to ** this interface is 0, then both the C-API and the SQL function are disabled. @@ -2324,23 +2339,30 @@ struct sqlite3_mem_methods { ** ** [[SQLITE_DBCONFIG_MAINDBNAME]]
SQLITE_DBCONFIG_MAINDBNAME
**
^This option is used to change the name of the "main" database -** schema. ^The sole argument is a pointer to a constant UTF8 string -** which will become the new schema name in place of "main". ^SQLite -** does not make a copy of the new main schema name string, so the application -** must ensure that the argument passed into this DBCONFIG option is unchanged -** until after the database connection closes. +** schema. This option does not follow the +** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format]. +** This option takes exactly one additional argument so that the +** [sqlite3_db_config()] call has a total of three parameters. The +** extra argument must be a pointer to a constant UTF8 string which +** will become the new schema name in place of "main". ^SQLite does +** not make a copy of the new main schema name string, so the application +** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME +** is unchanged until after the database connection closes. **
** ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] **
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
-**
Usually, when a database in wal mode is closed or detached from a -** database handle, SQLite checks if this will mean that there are now no -** connections at all to the database. If so, it performs a checkpoint -** operation before closing the connection. This option may be used to -** override this behavior. The first parameter passed to this operation -** is an integer - positive to disable checkpoints-on-close, or zero (the -** default) to enable them, and negative to leave the setting unchanged. -** The second parameter is a pointer to an integer +**
Usually, when a database in [WAL mode] is closed or detached from a +** database handle, SQLite checks if if there are other connections to the +** same database, and if there are no other database connection (if the +** connection being closed is the last open connection to the database), +** then SQLite performs a [checkpoint] before closing the connection and +** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can +** be used to override that behavior. The first argument passed to this +** operation (the third parameter to [sqlite3_db_config()]) is an integer +** which is positive to disable checkpoints-on-close, or zero (the default) +** to enable them, and negative to leave the setting unchanged. +** The second argument (the fourth parameter) is a pointer to an integer ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. **
@@ -2501,7 +2523,7 @@ struct sqlite3_mem_methods { ** statistics. For statistics to be collected, the flag must be set on ** the database handle both when the SQL statement is prepared and when it ** is stepped. The flag is set (collection of statistics is enabled) -** by default. This option takes two arguments: an integer and a pointer to +** by default.

This option takes two arguments: an integer and a pointer to ** an integer.. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the statement scanstatus option. If the second argument ** is not NULL, then the value of the statement scanstatus setting after @@ -2515,7 +2537,7 @@ struct sqlite3_mem_methods { ** in which tables and indexes are scanned so that the scans start at the end ** and work toward the beginning rather than starting at the beginning and ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the -** same as setting [PRAGMA reverse_unordered_selects]. This option takes +** same as setting [PRAGMA reverse_unordered_selects].

This option takes ** two arguments which are an integer and a pointer to an integer. The first ** argument is 1, 0, or -1 to enable, disable, or leave unchanged the ** reverse scan order flag, respectively. If the second argument is not NULL, @@ -2524,7 +2546,76 @@ struct sqlite3_mem_methods { ** first argument. ** ** +** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]] +**

SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE
+**
The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables +** the ability of the [ATTACH DATABASE] SQL command to create a new database +** file if the database filed named in the ATTACH command does not already +** exist. This ability of ATTACH to create a new database is enabled by +** default. Applications can disable or reenable the ability for ATTACH to +** create new database files using this DBCONFIG option.

+** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the attach-create flag, respectively. If the second +** argument is not NULL, then 0 or 1 is written into the integer that the +** second argument points to depending on if the attach-create flag is set +** after processing the first argument. +**

+** +** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]] +**
SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE
+**
The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the +** ability of the [ATTACH DATABASE] SQL command to open a database for writing. +** This capability is enabled by default. Applications can disable or +** reenable this capability using the current DBCONFIG option. If the +** the this capability is disabled, the [ATTACH] command will still work, +** but the database will be opened read-only. If this option is disabled, +** then the ability to create a new database using [ATTACH] is also disabled, +** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE] +** option.

+** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the ability to ATTACH another database for writing, +** respectively. If the second argument is not NULL, then 0 or 1 is written +** into the integer to which the second argument points, depending on whether +** the ability to ATTACH a read/write database is enabled or disabled +** after processing the first argument. +**

+** +** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]] +**
SQLITE_DBCONFIG_ENABLE_COMMENTS
+**
The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the +** ability to include comments in SQL text. Comments are enabled by default. +** An application can disable or reenable comments in SQL text using this +** DBCONFIG option.

+** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the ability to use comments in SQL text, +** respectively. If the second argument is not NULL, then 0 or 1 is written +** into the integer that the second argument points to depending on if +** comments are allowed in SQL text after processing the first argument. +**

+** **
+** +** [[DBCONFIG arguments]]

Arguments To SQLITE_DBCONFIG Options

+** +**

Most of the SQLITE_DBCONFIG options take two arguments, so that the +** overall call to [sqlite3_db_config()] has a total of four parameters. +** The first argument (the third parameter to sqlite3_db_config()) is a integer. +** The second argument is a pointer to an integer. If the first argument is 1, +** then the option becomes enabled. If the first integer argument is 0, then the +** option is disabled. If the first argument is -1, then the option setting +** is unchanged. The second argument, the pointer to an integer, may be NULL. +** If the second argument is not NULL, then a value of 0 or 1 is written into +** the integer to which the second argument points, depending on whether the +** setting is disabled or enabled after applying any changes specified by +** the first argument. +** +**

While most SQLITE_DBCONFIG options use the argument format +** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] +** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the +** documentation of those exceptional options for details. */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ @@ -2546,7 +2637,10 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -10749,8 +10843,9 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c /* ** CAPI3REF: Serialize a database ** -** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory -** that is a serialization of the S database on [database connection] D. +** The sqlite3_serialize(D,S,P,F) interface returns a pointer to +** memory that is a serialization of the S database on +** [database connection] D. If S is a NULL pointer, the main database is used. ** If P is not a NULL pointer, then the size of the database in bytes ** is written into *P. ** From ba8573494ab69e35f208f1c961ea7c9bf5aeab07 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 9 Feb 2025 09:36:57 -0800 Subject: [PATCH 05/25] Add shadcn, tailwind and react detection & templates to bun create. Also: `bun install --analyze ` (#17035) --- docs/bundler/executables.md | 2 + docs/bundler/loaders.md | 2 - docs/nav.ts | 11 +- packages/bun-types/bun.d.ts | 12 +- src/bake/DevServer.zig | 51 +- .../client/JavaScriptSyntaxHighlighter.css | 147 +++ .../client/JavaScriptSyntaxHighlighter.ts | 796 ++++++++++++++++ .../JavaScriptSyntaxHighlighterComponent.tsx | 41 + src/bun.js/api/BunObject.zig | 2 +- src/bun.js/api/server.zig | 70 +- src/bun.js/api/server/HTMLBundle.zig | 21 +- .../bindings/InspectorTestReporterAgent.cpp | 2 +- src/bun.js/bindings/ZigGlobalObject.cpp | 14 +- src/bun.js/bindings/bindings.cpp | 7 +- src/bun.js/bindings/headers-handwritten.h | 2 +- src/bun.js/javascript.zig | 13 +- src/bundler/bundle_v2.zig | 80 +- src/cli.zig | 7 +- src/cli/build_command.zig | 16 +- src/cli/create_command.zig | 103 +- src/cli/run_command.zig | 2 +- src/create/SourceFileProjectGenerator.zig | 881 ++++++++++++++++++ ...EPLACE_ME_WITH_YOUR_APP_FILE_NAME.build.ts | 169 ++++ ...LACE_ME_WITH_YOUR_APP_FILE_NAME.client.tsx | 25 + .../REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css | 1 + .../REPLACE_ME_WITH_YOUR_APP_FILE_NAME.html | 14 + .../REPLACE_ME_WITH_YOUR_APP_NAME.html | 14 + .../projects/react-shadcn-spa/bunfig.toml | 2 + .../projects/react-shadcn-spa/components.json | 21 + .../projects/react-shadcn-spa/lib/utils.ts | 6 + .../projects/react-shadcn-spa/package.json | 9 + .../react-shadcn-spa/styles/globals.css | 144 +++ .../react-shadcn-spa/styles/index.css | 1 + .../projects/react-shadcn-spa/tsconfig.json | 19 + .../REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css | 25 + src/create/projects/react-spa/package.json | 9 + .../REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css | 1 + src/css/error.zig | 6 +- src/install/install.zig | 285 ++++-- src/install/lifecycle_script_runner.zig | 3 +- src/js/internal/html.ts | 36 +- src/options.zig | 2 + src/output.zig | 35 + src/string_immutable.zig | 6 +- src/sys.zig | 148 ++- .../__snapshots__/create-jsx.test.ts.snap | 231 +++++ test/cli/create/create-jsx.test.ts | 366 ++++++++ .../components/Feature.jsx | 27 + .../components/Features.jsx | 56 ++ .../components/Footer.jsx | 35 + .../react-spa-no-tailwind/components/Hero.jsx | 48 + .../create/react-spa-no-tailwind/index.html | 12 + .../create/react-spa-no-tailwind/index.jsx | 22 + .../create/react-spa-no-tailwind/styles.css | 278 ++++++ test/cli/create/shadcn.tsx | 105 +++ test/cli/create/tailwind.tsx | 88 ++ test/cli/install/bun-add.test.ts | 148 ++- test/js/bun/http/bun-serve-html-entry.test.ts | 5 +- test/js/bun/http/bun-serve-html.test.ts | 2 +- test/js/bun/http/bun-serve-static-fixture.js | 8 +- test/js/bun/util/bun-file.test.ts | 19 +- .../http2-wrapper/http2-wrapper.test.ts | 2 + 62 files changed, 4435 insertions(+), 280 deletions(-) create mode 100644 src/bake/client/JavaScriptSyntaxHighlighter.css create mode 100644 src/bake/client/JavaScriptSyntaxHighlighter.ts create mode 100644 src/bake/client/JavaScriptSyntaxHighlighterComponent.tsx create mode 100644 src/create/SourceFileProjectGenerator.zig create mode 100644 src/create/projects/react-shadcn-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.build.ts create mode 100644 src/create/projects/react-shadcn-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.client.tsx create mode 100644 src/create/projects/react-shadcn-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css create mode 100644 src/create/projects/react-shadcn-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.html create mode 100644 src/create/projects/react-shadcn-spa/REPLACE_ME_WITH_YOUR_APP_NAME.html create mode 100644 src/create/projects/react-shadcn-spa/bunfig.toml create mode 100644 src/create/projects/react-shadcn-spa/components.json create mode 100644 src/create/projects/react-shadcn-spa/lib/utils.ts create mode 100644 src/create/projects/react-shadcn-spa/package.json create mode 100644 src/create/projects/react-shadcn-spa/styles/globals.css create mode 100644 src/create/projects/react-shadcn-spa/styles/index.css create mode 100644 src/create/projects/react-shadcn-spa/tsconfig.json create mode 100644 src/create/projects/react-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css create mode 100644 src/create/projects/react-spa/package.json create mode 100644 src/create/projects/react-tailwind-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css create mode 100644 test/cli/create/__snapshots__/create-jsx.test.ts.snap create mode 100644 test/cli/create/create-jsx.test.ts create mode 100644 test/cli/create/react-spa-no-tailwind/components/Feature.jsx create mode 100644 test/cli/create/react-spa-no-tailwind/components/Features.jsx create mode 100644 test/cli/create/react-spa-no-tailwind/components/Footer.jsx create mode 100644 test/cli/create/react-spa-no-tailwind/components/Hero.jsx create mode 100644 test/cli/create/react-spa-no-tailwind/index.html create mode 100644 test/cli/create/react-spa-no-tailwind/index.jsx create mode 100644 test/cli/create/react-spa-no-tailwind/styles.css create mode 100644 test/cli/create/shadcn.tsx create mode 100644 test/cli/create/tailwind.tsx diff --git a/docs/bundler/executables.md b/docs/bundler/executables.md index c477e6a82cbd8a..51c8963a54e7b2 100644 --- a/docs/bundler/executables.md +++ b/docs/bundler/executables.md @@ -196,6 +196,8 @@ import icon from "./icon.png" with { type: "file" }; import { file } from "bun"; const bytes = await file(icon).arrayBuffer(); +// await fs.promises.readFile(icon) +// fs.readFileSync(icon) ``` ### Embed SQLite databases diff --git a/docs/bundler/loaders.md b/docs/bundler/loaders.md index 2e399c984990f5..06cd4e01d83592 100644 --- a/docs/bundler/loaders.md +++ b/docs/bundler/loaders.md @@ -192,8 +192,6 @@ Otherwise, the database to embed is copied into the `outdir` with a hashed filen ### `html` -**HTML loader**. Default for `.html` after Bun v1.2.0. - The html loader processes HTML files and bundles any referenced assets. It will: - Bundle and hash referenced JavaScript files (` + + diff --git a/src/create/projects/react-shadcn-spa/REPLACE_ME_WITH_YOUR_APP_NAME.html b/src/create/projects/react-shadcn-spa/REPLACE_ME_WITH_YOUR_APP_NAME.html new file mode 100644 index 00000000000000..6fe77fb57a79be --- /dev/null +++ b/src/create/projects/react-shadcn-spa/REPLACE_ME_WITH_YOUR_APP_NAME.html @@ -0,0 +1,14 @@ + + + + + + REPLACE_ME_WITH_YOUR_APP_FILE_NAME | Powered by Bun + + + + +

+ + + diff --git a/src/create/projects/react-shadcn-spa/bunfig.toml b/src/create/projects/react-shadcn-spa/bunfig.toml new file mode 100644 index 00000000000000..55db1c435b65a0 --- /dev/null +++ b/src/create/projects/react-shadcn-spa/bunfig.toml @@ -0,0 +1,2 @@ +[serve.static] +plugins = ["bun-plugin-tailwind"] \ No newline at end of file diff --git a/src/create/projects/react-shadcn-spa/components.json b/src/create/projects/react-shadcn-spa/components.json new file mode 100644 index 00000000000000..c196761cdb09b7 --- /dev/null +++ b/src/create/projects/react-shadcn-spa/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "styles/globals.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/src/create/projects/react-shadcn-spa/lib/utils.ts b/src/create/projects/react-shadcn-spa/lib/utils.ts new file mode 100644 index 00000000000000..a5ef193506d07d --- /dev/null +++ b/src/create/projects/react-shadcn-spa/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/src/create/projects/react-shadcn-spa/package.json b/src/create/projects/react-shadcn-spa/package.json new file mode 100644 index 00000000000000..bc1f1f35bc864c --- /dev/null +++ b/src/create/projects/react-shadcn-spa/package.json @@ -0,0 +1,9 @@ +{ + "name": "react-tailwind-spa", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "bun './**/*.html'", + "build": "bun 'REPLACE_ME_WITH_YOUR_APP_FILE_NAME.build.ts'" + } +} diff --git a/src/create/projects/react-shadcn-spa/styles/globals.css b/src/create/projects/react-shadcn-spa/styles/globals.css new file mode 100644 index 00000000000000..dd34342a717348 --- /dev/null +++ b/src/create/projects/react-shadcn-spa/styles/globals.css @@ -0,0 +1,144 @@ +@import "tailwindcss"; + +@plugin "tailwindcss-animate"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: hsl(0 0% 100%); + --foreground: hsl(240 10% 3.9%); + --card: hsl(0 0% 100%); + --card-foreground: hsl(240 10% 3.9%); + --popover: hsl(0 0% 100%); + --popover-foreground: hsl(240 10% 3.9%); + --primary: hsl(240 5.9% 10%); + --primary-foreground: hsl(0 0% 98%); + --secondary: hsl(240 4.8% 95.9%); + --secondary-foreground: hsl(240 5.9% 10%); + --muted: hsl(240 4.8% 95.9%); + --muted-foreground: hsl(240 3.8% 46.1%); + --accent: hsl(240 4.8% 95.9%); + --accent-foreground: hsl(240 5.9% 10%); + --destructive: hsl(0 84.2% 60.2%); + --destructive-foreground: hsl(0 0% 98%); + --border: hsl(240 5.9% 90%); + --input: hsl(240 5.9% 90%); + --ring: hsl(240 10% 3.9%); + --chart-1: hsl(12 76% 61%); + --chart-2: hsl(173 58% 39%); + --chart-3: hsl(197 37% 24%); + --chart-4: hsl(43 74% 66%); + --chart-5: hsl(27 87% 67%); + --radius: 0.6rem; + --sidebar-background: hsl(0 0% 98%); + --sidebar-foreground: hsl(240 5.3% 26.1%); + --sidebar-primary: hsl(240 5.9% 10%); + --sidebar-primary-foreground: hsl(0 0% 98%); + --sidebar-accent: hsl(240 4.8% 95.9%); + --sidebar-accent-foreground: hsl(240 5.9% 10%); + --sidebar-border: hsl(220 13% 91%); + --sidebar-ring: hsl(217.2 91.2% 59.8%); +} + +.dark { + --background: hsl(240 10% 3.9%); + --foreground: hsl(0 0% 98%); + --card: hsl(240 10% 3.9%); + --card-foreground: hsl(0 0% 98%); + --popover: hsl(240 10% 3.9%); + --popover-foreground: hsl(0 0% 98%); + --primary: hsl(0 0% 98%); + --primary-foreground: hsl(240 5.9% 10%); + --secondary: hsl(240 3.7% 15.9%); + --secondary-foreground: hsl(0 0% 98%); + --muted: hsl(240 3.7% 15.9%); + --muted-foreground: hsl(240 5% 64.9%); + --accent: hsl(240 3.7% 15.9%); + --accent-foreground: hsl(0 0% 98%); + --destructive: hsl(0 62.8% 30.6%); + --destructive-foreground: hsl(0 0% 98%); + --border: hsl(240 3.7% 15.9%); + --input: hsl(240 3.7% 15.9%); + --ring: hsl(240 4.9% 83.9%); + --chart-1: hsl(220 70% 50%); + --chart-2: hsl(160 60% 45%); + --chart-3: hsl(30 80% 55%); + --chart-4: hsl(280 65% 60%); + --chart-5: hsl(340 75% 55%); + --sidebar-background: hsl(240 5.9% 10%); + --sidebar-foreground: hsl(240 4.8% 95.9%); + --sidebar-primary: hsl(224.3 76.3% 48%); + --sidebar-primary-foreground: hsl(0 0% 100%); + --sidebar-accent: hsl(240 3.7% 15.9%); + --sidebar-accent-foreground: hsl(240 4.8% 95.9%); + --sidebar-border: hsl(240 3.7% 15.9%); + --sidebar-ring: hsl(217.2 91.2% 59.8%); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar-background); + --animate-accordion-down: accordion-down 0.2s ease-out; + --animate-accordion-up: accordion-up 0.2s ease-out; + + @keyframes accordion-down { + from { + height: 0; + } + to { + height: var(--radix-accordion-content-height); + } + } + + @keyframes accordion-up { + from { + height: var(--radix-accordion-content-height); + } + to { + height: 0; + } + } +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/src/create/projects/react-shadcn-spa/styles/index.css b/src/create/projects/react-shadcn-spa/styles/index.css new file mode 100644 index 00000000000000..465ba76d3409ab --- /dev/null +++ b/src/create/projects/react-shadcn-spa/styles/index.css @@ -0,0 +1 @@ +@import "../styles/globals.css"; diff --git a/src/create/projects/react-shadcn-spa/tsconfig.json b/src/create/projects/react-shadcn-spa/tsconfig.json new file mode 100644 index 00000000000000..2c4128af6b51c9 --- /dev/null +++ b/src/create/projects/react-shadcn-spa/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "module": "preserve", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + } + }, + "include": ["**/*.ts", "**/*.tsx"] +} diff --git a/src/create/projects/react-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css b/src/create/projects/react-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css new file mode 100644 index 00000000000000..15c3b8c3463b1a --- /dev/null +++ b/src/create/projects/react-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css @@ -0,0 +1,25 @@ +* { + box-sizing: border-box; +} + +:root { + --font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen; + --mono-font-family: "Fira Code", "Hack", Menlo, Monaco, "Lucida Console", "Liberation Mono", "Courier New", monospace; +} + +body { + margin: 0; + padding: 0; + + font-family: var(--font-family); +} + +#root { + width: 100%; + height: 100%; +} + +code, +pre { + font-family: var(--mono-font-family); +} diff --git a/src/create/projects/react-spa/package.json b/src/create/projects/react-spa/package.json new file mode 100644 index 00000000000000..638cc803e9281e --- /dev/null +++ b/src/create/projects/react-spa/package.json @@ -0,0 +1,9 @@ +{ + "name": "react-spa", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "bun './**/*.html'", + "build": "bun build --target=browser REPLACE_ME_WITH_YOUR_APP_FILE_NAME.html --outdir dist" + } +} diff --git a/src/create/projects/react-tailwind-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css b/src/create/projects/react-tailwind-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css new file mode 100644 index 00000000000000..f1d8c73cdcf9ea --- /dev/null +++ b/src/create/projects/react-tailwind-spa/REPLACE_ME_WITH_YOUR_APP_FILE_NAME.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/src/css/error.zig b/src/css/error.zig index 67152e7cfd17e7..4f5cbd40295300 100644 --- a/src/css/error.zig +++ b/src/css/error.zig @@ -77,12 +77,12 @@ pub fn Err(comptime T: type) type { }; } - pub fn addToLogger(this: @This(), log: *logger.Log, source: *const logger.Source) !void { + pub fn addToLogger(this: @This(), log: *logger.Log, source: *const logger.Source, allocator: std.mem.Allocator) !void { try log.addMsg(.{ .kind = .err, .data = .{ - .location = if (this.loc) |*loc| try loc.toLocation(source, log.msgs.allocator) else null, - .text = try std.fmt.allocPrint(log.msgs.allocator, "{}", .{this.kind}), + .location = if (this.loc) |*loc| try loc.toLocation(source, allocator) else null, + .text = try std.fmt.allocPrint(allocator, "{}", .{this.kind}), }, }); diff --git a/src/install/install.zig b/src/install/install.zig index f5a268871543b3..ce8ad53dae50b4 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -2844,12 +2844,12 @@ pub const PackageManager = struct { const package_name = active_lifecycle_script_running_for_the_longest_amount_of_time.package_name; if (!(package_name.len > 1 and package_name[package_name.len - 1] == 's')) { - Output.warn("{s}'s postinstall has costed you {}\n", .{ + Output.warn("{s}'s postinstall cost you {}\n", .{ package_name, bun.fmt.fmtDurationOneDecimal(time_running), }); } else { - Output.warn("{s}' postinstall has costed you {}\n", .{ + Output.warn("{s}' postinstall cost you {}\n", .{ package_name, bun.fmt.fmtDurationOneDecimal(time_running), }); @@ -7334,6 +7334,7 @@ pub const PackageManager = struct { base = registry; } } + if (base.url.len == 0) base.url = Npm.Registry.default_url; this.scope = try Npm.Registry.Scope.fromAPI("", base, allocator, env); defer { @@ -7541,6 +7542,9 @@ pub const PackageManager = struct { } if (maybe_cli) |cli| { + this.do.analyze = cli.analyze; + this.enable.only_missing = cli.only_missing or cli.analyze; + if (cli.registry.len > 0) { this.scope.url = URL.parse(cli.registry); } @@ -7727,6 +7731,7 @@ pub const PackageManager = struct { summary: bool = true, trust_dependencies_from_args: bool = false, update_to_latest: bool = false, + analyze: bool = false, }; pub const Enable = packed struct { @@ -7743,6 +7748,7 @@ pub const PackageManager = struct { force_install: bool = false, exact_versions: bool = false, + only_missing: bool = false, }; }; @@ -8157,7 +8163,7 @@ pub const PackageManager = struct { /// if options.add_trusted_dependencies is true, gets list from PackageManager.trusted_deps_to_add_to_package_json pub fn edit( manager: *PackageManager, - updates: []UpdateRequest, + updates: *[]UpdateRequest, current_package_json: *Expr, dependency_list: string, options: EditOptions, @@ -8171,6 +8177,7 @@ pub const PackageManager = struct { const allocator = manager.allocator; var remaining = updates.len; var replacing: usize = 0; + const only_add_missing = manager.options.enable.only_missing; // There are three possible scenarios here // 1. There is no "dependencies" (or equivalent list) or it is empty @@ -8201,61 +8208,74 @@ pub const PackageManager = struct { } } } + { + var i: usize = 0; + loop: while (i < updates.len) { + var request = &updates.*[i]; + inline for ([_]string{ "dependencies", "devDependencies", "optionalDependencies", "peerDependencies" }) |list| { + if (current_package_json.asProperty(list)) |query| { + if (query.expr.data == .e_object) { + const name = if (request.is_aliased) + request.name + else + request.version.literal.slice(request.version_buf); - for (updates) |*request| { - inline for ([_]string{ "dependencies", "devDependencies", "optionalDependencies", "peerDependencies" }) |list| { - if (current_package_json.asProperty(list)) |query| { - if (query.expr.data == .e_object) { - const name = if (request.is_aliased) - request.name - else - request.version.literal.slice(request.version_buf); - - if (query.expr.asProperty(name)) |value| { - if (value.expr.data == .e_string) { - if (request.package_id != invalid_package_id and strings.eqlLong(list, dependency_list, true)) { - replacing += 1; - } else { - if (manager.subcommand == .update and options.before_install) add_packages_to_update: { - const version_literal = try value.expr.asStringCloned(allocator) orelse break :add_packages_to_update; - var tag = Dependency.Version.Tag.infer(version_literal); + if (query.expr.asProperty(name)) |value| { + if (value.expr.data == .e_string) { + if (request.package_id != invalid_package_id and strings.eqlLong(list, dependency_list, true)) { + replacing += 1; + } else { + if (manager.subcommand == .update and options.before_install) add_packages_to_update: { + const version_literal = try value.expr.asStringCloned(allocator) orelse break :add_packages_to_update; + var tag = Dependency.Version.Tag.infer(version_literal); - if (tag != .npm and tag != .dist_tag) break :add_packages_to_update; + if (tag != .npm and tag != .dist_tag) break :add_packages_to_update; - const entry = manager.updating_packages.getOrPut(allocator, name) catch bun.outOfMemory(); + const entry = manager.updating_packages.getOrPut(allocator, name) catch bun.outOfMemory(); - // first come, first serve - if (entry.found_existing) break :add_packages_to_update; + // first come, first serve + if (entry.found_existing) break :add_packages_to_update; - var is_alias = false; - if (strings.hasPrefixComptime(strings.trim(version_literal, &strings.whitespace_chars), "npm:")) { - if (strings.lastIndexOfChar(version_literal, '@')) |at_index| { - tag = Dependency.Version.Tag.infer(version_literal[at_index + 1 ..]); - if (tag != .npm and tag != .dist_tag) break :add_packages_to_update; - is_alias = true; + var is_alias = false; + if (strings.hasPrefixComptime(strings.trim(version_literal, &strings.whitespace_chars), "npm:")) { + if (strings.lastIndexOfChar(version_literal, '@')) |at_index| { + tag = Dependency.Version.Tag.infer(version_literal[at_index + 1 ..]); + if (tag != .npm and tag != .dist_tag) break :add_packages_to_update; + is_alias = true; + } } + + entry.value_ptr.* = .{ + .original_version_literal = version_literal, + .is_alias = is_alias, + .original_version = null, + }; } + if (!only_add_missing) { + request.e_string = value.expr.data.e_string; + remaining -= 1; + } else { + if (i < updates.*.len - 1) { + updates.*[i] = updates.*[updates.*.len - 1]; + } - entry.value_ptr.* = .{ - .original_version_literal = version_literal, - .is_alias = is_alias, - .original_version = null, - }; + updates.*.len -= 1; + remaining -= 1; + continue :loop; + } } - request.e_string = value.expr.data.e_string; - remaining -= 1; } - } - break; - } else { - if (request.version.tag == .github or request.version.tag == .git) { - for (query.expr.data.e_object.properties.slice()) |item| { - if (item.value) |v| { - const url = request.version.literal.slice(request.version_buf); - if (v.data == .e_string and v.data.e_string.eql(string, url)) { - request.e_string = v.data.e_string; - remaining -= 1; - break; + break; + } else { + if (request.version.tag == .github or request.version.tag == .git) { + for (query.expr.data.e_object.properties.slice()) |item| { + if (item.value) |v| { + const url = request.version.literal.slice(request.version_buf); + if (v.data == .e_string and v.data.e_string.eql(string, url)) { + request.e_string = v.data.e_string; + remaining -= 1; + break; + } } } } @@ -8263,6 +8283,7 @@ pub const PackageManager = struct { } } } + i += 1; } } } @@ -8324,7 +8345,7 @@ pub const PackageManager = struct { break :brk deps; }; - outer: for (updates) |*request| { + outer: for (updates.*) |*request| { if (request.e_string != null) continue; defer if (comptime Environment.allow_assert) bun.assert(request.e_string != null); @@ -8491,7 +8512,7 @@ pub const PackageManager = struct { } const resolutions = if (!options.before_install) manager.lockfile.packages.items(.resolution) else &.{}; - for (updates) |*request| { + for (updates.*) |*request| { if (request.e_string) |e_string| { if (request.package_id >= resolutions.len or resolutions[request.package_id].tag == .uninitialized) { e_string.data = uninitialized: { @@ -9610,6 +9631,8 @@ pub const PackageManager = struct { clap.parseParam("--peer Add dependency to \"peerDependencies\"") catch unreachable, clap.parseParam("-E, --exact Add the exact version instead of the ^range") catch unreachable, clap.parseParam("--filter ... Install packages for the matching workspaces") catch unreachable, + clap.parseParam("-a, --analyze Analyze & install all dependencies of files passed as arguments recursively (using Bun's bundler)") catch unreachable, + clap.parseParam("--only-missing Only add dependencies to package.json if they are not already present") catch unreachable, clap.parseParam(" ... ") catch unreachable, }); @@ -9632,6 +9655,8 @@ pub const PackageManager = struct { clap.parseParam("--optional Add dependency to \"optionalDependencies\"") catch unreachable, clap.parseParam("--peer Add dependency to \"peerDependencies\"") catch unreachable, clap.parseParam("-E, --exact Add the exact version instead of the ^range") catch unreachable, + clap.parseParam("-a, --analyze Recursively analyze & install dependencies of files passed as arguments (using Bun's bundler)") catch unreachable, + clap.parseParam("--only-missing Only add dependencies to package.json if they are not already present") catch unreachable, clap.parseParam(" ... \"name\" or \"name@version\" of package(s) to install") catch unreachable, }); @@ -9688,7 +9713,8 @@ pub const PackageManager = struct { config: ?string = null, network_concurrency: ?u16 = null, backend: ?PackageInstall.Method = null, - + analyze: bool = false, + only_missing: bool = false, positionals: []const string = &[_]string{}, yarn: bool = false, @@ -10201,6 +10227,8 @@ pub const PackageManager = struct { cli.optional = args.flag("--optional"); cli.peer = args.flag("--peer"); cli.exact = args.flag("--exact"); + cli.analyze = args.flag("--analyze"); + cli.only_missing = args.flag("--only-missing"); } if (args.option("--concurrent-scripts")) |concurrency| { @@ -10275,6 +10303,11 @@ pub const PackageManager = struct { Global.crash(); } + if (cli.analyze and cli.positionals.len == 0) { + Output.errGeneric("Missing script(s) to analyze. Pass paths to scripts to analyze their dependencies and add any missing ones to the lockfile.\n", .{}); + Global.crash(); + } + return cli; } }; @@ -10507,9 +10540,59 @@ pub const PackageManager = struct { ctx: Command.Context, subcommand: Subcommand, ) !void { - const cli = switch (subcommand) { + var cli = switch (subcommand) { inline else => |cmd| try PackageManager.CommandLineArguments.parse(ctx.allocator, cmd), }; + + // The way this works: + // 1. Run the bundler on source files + // 2. Rewrite positional arguments to act identically to the developer + // typing in the dependency names + // 3. Run the install command + if (cli.analyze) { + const Analyzer = struct { + ctx: Command.Context, + cli: *PackageManager.CommandLineArguments, + subcommand: Subcommand, + pub fn onAnalyze( + this: *@This(), + result: *bun.bundle_v2.BundleV2.DependenciesScanner.Result, + ) anyerror!void { + // TODO: add separate argument that makes it so positionals[1..] is not done and instead the positionals are passed + var positionals = bun.default_allocator.alloc(string, result.dependencies.keys().len + 1) catch bun.outOfMemory(); + positionals[0] = "add"; + bun.copy(string, positionals[1..], result.dependencies.keys()); + this.cli.positionals = positionals; + + try updatePackageJSONAndInstallAndCLI(this.ctx, this.subcommand, this.cli.*); + + Global.exit(0); + } + }; + var analyzer = Analyzer{ + .ctx = ctx, + .cli = &cli, + .subcommand = subcommand, + }; + var fetcher = bun.bundle_v2.BundleV2.DependenciesScanner{ + .ctx = &analyzer, + .entry_points = cli.positionals[1..], + .onFetch = @ptrCast(&Analyzer.onAnalyze), + }; + + // This runs the bundler. + try bun.CLI.BuildCommand.exec(bun.CLI.Command.get(), &fetcher); + return; + } + + return updatePackageJSONAndInstallAndCLI(ctx, subcommand, cli); + } + + fn updatePackageJSONAndInstallAndCLI( + ctx: Command.Context, + subcommand: Subcommand, + cli: CommandLineArguments, + ) !void { var manager, const original_cwd = init(ctx, cli, subcommand) catch |err| brk: { if (err == error.MissingPackageJSON) { switch (subcommand) { @@ -10707,23 +10790,40 @@ pub const PackageManager = struct { } } - const updates: []UpdateRequest = if (manager.subcommand == .@"patch-commit" or manager.subcommand == .patch) + return try updatePackageJSONAndInstallWithManagerWithUpdatesAndUpdateRequests( + manager, + ctx, + original_cwd, + manager.options.positionals[1..], + &update_requests, + log_level, + ); + } + + fn updatePackageJSONAndInstallWithManagerWithUpdatesAndUpdateRequests( + manager: *PackageManager, + ctx: Command.Context, + original_cwd: string, + positionals: []const string, + update_requests: *UpdateRequest.Array, + comptime log_level: Options.LogLevel, + ) !void { + var updates: []UpdateRequest = if (manager.subcommand == .@"patch-commit" or manager.subcommand == .patch) &[_]UpdateRequest{} else - UpdateRequest.parse(ctx.allocator, manager, ctx.log, manager.options.positionals[1..], &update_requests, manager.subcommand); + UpdateRequest.parse(ctx.allocator, manager, ctx.log, positionals, update_requests, manager.subcommand); try manager.updatePackageJSONAndInstallWithManagerWithUpdates( ctx, - updates, + &updates, manager.subcommand, original_cwd, log_level, ); } - fn updatePackageJSONAndInstallWithManagerWithUpdates( manager: *PackageManager, ctx: Command.Context, - updates: []UpdateRequest, + updates: *[]UpdateRequest, subcommand: Subcommand, original_cwd: string, comptime log_level: Options.LogLevel, @@ -10800,7 +10900,7 @@ pub const PackageManager = struct { .remove => { // if we're removing, they don't have to specify where it is installed in the dependencies list // they can even put it multiple times and we will just remove all of them - for (updates) |request| { + for (updates.*) |request| { inline for ([_]string{ "dependencies", "devDependencies", "optionalDependencies", "peerDependencies" }) |list| { if (current_package_json.root.asProperty(list)) |query| { if (query.expr.data == .e_object) { @@ -10892,7 +10992,12 @@ pub const PackageManager = struct { } manager.to_update = subcommand == .update; - manager.update_requests = updates; + + { + // Incase it's a pointer to self. Avoid RLS. + const cloned = updates.*; + manager.update_requests = cloned; + } var buffer_writer = try JSPrinter.BufferWriter.init(manager.allocator); try buffer_writer.buffer.list.ensureTotalCapacity(manager.allocator, current_package_json.source.contents.len + 1); @@ -10996,7 +11101,7 @@ pub const PackageManager = struct { try manager.installWithManager(ctx, root_package_json_source, original_cwd, log_level); if (subcommand == .update or subcommand == .add or subcommand == .link) { - for (updates) |request| { + for (updates.*) |request| { if (request.failed) { Global.exit(1); return; @@ -11085,7 +11190,7 @@ pub const PackageManager = struct { bun.copy(u8, &node_modules_buf, "node_modules" ++ std.fs.path.sep_str); const offset_buf = node_modules_buf["node_modules/".len..]; const name_hashes = manager.lockfile.packages.items(.name_hash); - for (updates) |request| { + for (updates.*) |request| { // If the package no longer exists in the updated lockfile, delete the directory // This is not thorough. // It does not handle nested dependencies @@ -12223,8 +12328,48 @@ pub const PackageManager = struct { pub var package_json_cwd: string = ""; pub fn install(ctx: Command.Context) !void { - const cli = try CommandLineArguments.parse(ctx.allocator, .install); + var cli = try CommandLineArguments.parse(ctx.allocator, .install); + + // The way this works: + // 1. Run the bundler on source files + // 2. Rewrite positional arguments to act identically to the developer + // typing in the dependency names + // 3. Run the install command + if (cli.analyze) { + const Analyzer = struct { + ctx: Command.Context, + cli: *CommandLineArguments, + pub fn onAnalyze(this: *@This(), result: *bun.bundle_v2.BundleV2.DependenciesScanner.Result) anyerror!void { + // TODO: add separate argument that makes it so positionals[1..] is not done and instead the positionals are passed + var positionals = bun.default_allocator.alloc(string, result.dependencies.keys().len + 1) catch bun.outOfMemory(); + positionals[0] = "install"; + bun.copy(string, positionals[1..], result.dependencies.keys()); + this.cli.positionals = positionals; + + try installWithCLI(this.ctx, this.cli.*); + + Global.exit(0); + } + }; + var analyzer = Analyzer{ + .ctx = ctx, + .cli = &cli, + }; + + var fetcher = bun.bundle_v2.BundleV2.DependenciesScanner{ + .ctx = &analyzer, + .entry_points = cli.positionals[1..], + .onFetch = @ptrCast(&Analyzer.onAnalyze), + }; + try bun.CLI.BuildCommand.exec(bun.CLI.Command.get(), &fetcher); + return; + } + + return installWithCLI(ctx, cli); + } + + pub fn installWithCLI(ctx: Command.Context, cli: CommandLineArguments) !void { const subcommand: Subcommand = if (cli.positionals.len > 1) .add else .install; // TODO(dylan-conway): print `bun install ` or `bun add ` before logs from `init`. @@ -15247,13 +15392,15 @@ pub const PackageManager = struct { } else if (install_summary.skipped > 0 and install_summary.fail == 0 and this.update_requests.len == 0) { const count = @as(PackageID, @truncate(this.lockfile.packages.len)); if (count != install_summary.skipped) { - Output.pretty("Checked {d} install{s} across {d} package{s} (no changes) ", .{ - install_summary.skipped, - if (install_summary.skipped == 1) "" else "s", - count, - if (count == 1) "" else "s", - }); - Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp()); + if (!this.options.enable.only_missing) { + Output.pretty("Checked {d} install{s} across {d} package{s} (no changes) ", .{ + install_summary.skipped, + if (install_summary.skipped == 1) "" else "s", + count, + if (count == 1) "" else "s", + }); + Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp()); + } printed_timestamp = true; printBlockedPackagesInfo(install_summary, this.options.global); } else { diff --git a/src/install/lifecycle_script_runner.zig b/src/install/lifecycle_script_runner.zig index 52f10c548c5209..0ba772b9df6d35 100644 --- a/src/install/lifecycle_script_runner.zig +++ b/src/install/lifecycle_script_runner.zig @@ -146,8 +146,7 @@ pub const LifecycleScriptSubprocess = struct { const combined_script: [:0]u8 = copy_script.items[0 .. copy_script.items.len - 1 :0]; if (this.foreground and this.manager.options.log_level != .silent) { - Output.prettyError("$ {s}\n", .{combined_script}); - Output.flush(); + Output.command(combined_script); } else if (manager.scripts_node) |scripts_node| { manager.setNodeName( scripts_node, diff --git a/src/js/internal/html.ts b/src/js/internal/html.ts index d1f2f9cf67ef29..f054afd4f17784 100644 --- a/src/js/internal/html.ts +++ b/src/js/internal/html.ts @@ -75,12 +75,19 @@ yourself with Bun.serve(). for (const file of glob.scanSync(cwd)) { let resolved = path.resolve(cwd, file); + if (resolved.includes(path.sep + "node_modules" + path.sep)) { + continue; + } try { resolved = Bun.resolveSync(resolved, cwd); } catch { resolved = Bun.resolveSync("./" + resolved, cwd); } + if (resolved.includes(path.sep + "node_modules" + path.sep)) { + continue; + } + args.push(resolved); } } else { @@ -91,6 +98,10 @@ yourself with Bun.serve(). resolved = Bun.resolveSync("./" + arg, cwd); } + if (resolved.includes(path.sep + "node_modules" + path.sep)) { + continue; + } + args.push(resolved); } @@ -103,6 +114,10 @@ yourself with Bun.serve(). throw new Error("No HTML files found matching " + JSON.stringify(Bun.main)); } + args.sort((a, b) => { + return a.localeCompare(b); + }); + // Add cwd to find longest common path let needsPop = false; if (args.length === 1) { @@ -181,7 +196,7 @@ yourself with Bun.serve(). // If you're only providing one entry point, then match everything to it. // (except for assets, which have higher precedence) - if (htmlImports.length === 1 && servePaths[0] === "") { + if (htmlImports.length === 1) { servePaths[0] = "*"; } @@ -247,13 +262,28 @@ yourself with Bun.serve(). const elapsed = (performance.now() - initial).toFixed(2); const enableANSIColors = Bun.enableANSIColors; function printInitialMessage(isFirst: boolean) { + let pathnameToPrint; + if (servePaths.length === 1) { + pathnameToPrint = servePaths[0]; + } else { + const indexRoute = servePaths.find(a => { + return a === "index" || a === "" || a === "/"; + }); + pathnameToPrint = indexRoute !== undefined ? indexRoute : servePaths[0]; + } + + pathnameToPrint ||= "/"; + if (pathnameToPrint === "*") { + pathnameToPrint = "/"; + } + if (enableANSIColors) { let topLine = `${server.development ? "\x1b[34;7m DEV \x1b[0m " : ""}\x1b[1;34m\x1b[5mBun\x1b[0m \x1b[1;34mv${Bun.version}\x1b[0m`; if (isFirst) { topLine += ` \x1b[2mready in\x1b[0m \x1b[1m${elapsed}\x1b[0m ms`; } console.log(topLine + "\n"); - console.log(`\x1b[1;34m➜\x1b[0m \x1b[36m${server!.url.href}\x1b[0m`); + console.log(`\x1b[1;34m➜\x1b[0m \x1b[36m${new URL(pathnameToPrint, server!.url)}\x1b[0m`); } else { let topLine = `Bun v${Bun.version}`; if (isFirst) { @@ -263,7 +293,7 @@ yourself with Bun.serve(). topLine += ` ready in ${elapsed} ms`; } console.log(topLine + "\n"); - console.log(`url: ${server!.url.href}`); + console.log(`url: ${new URL(pathnameToPrint, server!.url)}`); } if (htmlImports.length > 1 || (servePaths[0] !== "" && servePaths[0] !== "*")) { console.log("\nRoutes:"); diff --git a/src/options.zig b/src/options.zig index 5388884980653d..a8338ad52432a2 100644 --- a/src/options.zig +++ b/src/options.zig @@ -1590,6 +1590,8 @@ pub const BundleOptions = struct { /// "react-jsx" or "react-jsx-dev-runtime") force_node_env: ForceNodeEnv = .unspecified, + ignore_module_resolution_errors: bool = false, + pub const ForceNodeEnv = enum { unspecified, development, diff --git a/src/output.zig b/src/output.zig index 5f793d4c7b670b..86c097649229d2 100644 --- a/src/output.zig +++ b/src/output.zig @@ -997,6 +997,41 @@ pub fn prettyErrorln(comptime fmt: string, args: anytype) void { prettyWithPrinter(fmt, args, printErrorln, .stderr); } +/// Pretty-print a command that will be run. +/// $ bun run foo +fn printCommand(argv: anytype, comptime destination: Destination) void { + const prettyFn = if (destination == .stdout) pretty else prettyError; + const printFn = if (destination == .stdout) print else printError; + switch (@TypeOf(argv)) { + [][:0]const u8, []const []const u8, []const []u8, [][]const u8 => { + prettyFn("$ ", .{}); + printFn("{s}", .{argv[0]}); + if (argv.len > 1) { + for (argv[1..]) |arg| { + printFn(" {s}", .{arg}); + } + } + prettyFn("\n", .{}); + }, + []const u8, []u8, [:0]const u8, [:0]u8 => { + prettyFn("$ {s}\n", .{argv}); + }, + else => { + @compileLog(argv); + @compileError("command() was given unsupported type: " ++ @typeName(@TypeOf(argv))); + }, + } + flush(); +} + +pub fn commandOut(argv: anytype) void { + printCommand(argv, .stdout); +} + +pub fn command(argv: anytype) void { + printCommand(argv, .stderr); +} + pub const Destination = enum(u8) { stderr, stdout, diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 365d1acda0bc56..7f85bb7e7808af 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -190,8 +190,12 @@ pub inline fn containsAny(in: anytype, target: string) bool { /// a folder name. Therefore, the name can't contain any non-URL-safe /// characters. pub fn isNPMPackageName(target: string) bool { - if (target.len == 0) return false; if (target.len > 214) return false; + return isNPMPackageNameIgnoreLength(target); +} + +pub fn isNPMPackageNameIgnoreLength(target: string) bool { + if (target.len == 0) return false; const scoped = switch (target[0]) { // Old packages may have capital letters diff --git a/src/sys.zig b/src/sys.zig index 1062fcffbcd297..7221d32973bf33 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -1671,7 +1671,7 @@ pub fn openatWindowsA( pub fn openatOSPath(dirfd: bun.FileDescriptor, file_path: bun.OSPathSliceZ, flags: i32, perm: bun.Mode) Maybe(bun.FileDescriptor) { if (comptime Environment.isMac) { // https://opensource.apple.com/source/xnu/xnu-7195.81.3/libsyscall/wrappers/open-base.c - const rc = syscall.@"openat$NOCANCEL"(dirfd.cast(), file_path.ptr, @as(c_uint, @intCast(flags)), @as(c_int, @intCast(perm))); + const rc = syscall.@"openat$NOCANCEL"(dirfd.cast(), file_path.ptr, @bitCast(bun.O.toPacked(flags)), perm); if (comptime Environment.allow_assert) log("openat({}, {s}, {d}) = {d}", .{ dirfd, bun.sliceTo(file_path, 0), flags, rc }); @@ -3161,81 +3161,13 @@ pub fn faccessat(dir_: anytype, subpath: anytype) JSC.Maybe(bool) { pub fn directoryExistsAt(dir: anytype, subpath: anytype) JSC.Maybe(bool) { const dir_fd = bun.toFD(dir); - if (comptime Environment.isWindows) { - const wbuf = bun.WPathBufferPool.get(); - defer bun.WPathBufferPool.put(wbuf); - const path = if (std.meta.Child(@TypeOf(subpath)) == u16) - bun.strings.toNTPath16(wbuf, subpath) + return switch (existsAtType(dir_fd, subpath)) { + // + .err => |err| if (err.getErrno() == .NOENT) + .{ .result = false } else - bun.strings.toNTPath(wbuf, subpath); - - const path_len_bytes: u16 = @truncate(path.len * 2); - var nt_name = w.UNICODE_STRING{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @constCast(path.ptr), - }; - var attr = w.OBJECT_ATTRIBUTES{ - .Length = @sizeOf(w.OBJECT_ATTRIBUTES), - .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(path)) - null - else if (dir_fd == bun.invalid_fd) - std.fs.cwd().fd - else - dir_fd.cast(), - .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. - .ObjectName = &nt_name, - .SecurityDescriptor = null, - .SecurityQualityOfService = null, - }; - var basic_info: w.FILE_BASIC_INFORMATION = undefined; - const rc = kernel32.NtQueryAttributesFile(&attr, &basic_info); - if (rc == .OBJECT_NAME_INVALID or rc == .BAD_NETWORK_PATH) { - bun.Output.warn("internal error: {s}: {}", .{ @tagName(rc), bun.fmt.fmtOSPath(path, .{}) }); - } - if (JSC.Maybe(bool).errnoSys(rc, .access)) |err| { - syslog("NtQueryAttributesFile({}, {}, O_DIRECTORY | O_RDONLY, 0) = {} {d}", .{ dir_fd, bun.fmt.fmtOSPath(path, .{}), err, rc }); - return err; - } - - const is_dir = basic_info.FileAttributes != kernel32.INVALID_FILE_ATTRIBUTES and - basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_DIRECTORY != 0 and - basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_READONLY == 0; - syslog("NtQueryAttributesFile({}, {}, O_DIRECTORY | O_RDONLY, 0) = {d}", .{ dir_fd, bun.fmt.fmtOSPath(path, .{}), @intFromBool(is_dir) }); - - return .{ .result = is_dir }; - } - - // TODO: use statx to query less information. this path is currently broken - // const have_statx = Environment.isLinux; - // if (have_statx) brk: { - // var statx: std.os.linux.Statx = undefined; - // if (Maybe(bool).errnoSys(bun.C.linux.statx( - // dir_fd.cast(), - // subpath, - // // Don't follow symlinks, don't automount, minimize permissions needed - // std.os.linux.AT.SYMLINK_NOFOLLOW | std.os.linux.AT.NO_AUTOMOUNT, - // // We only need the file type to check if it's a directory - // std.os.linux.STATX_TYPE, - // &statx, - // ), .statx)) |err| { - // switch (err.err.getErrno()) { - // .OPNOTSUPP, .NOSYS => break :brk, // Linux < 4.11 - // // truly doesn't exist. - // .NOENT => return .{ .result = false }, - // else => return err, - // } - // return err; - // } - // return .{ .result = S.ISDIR(statx.mode) }; - // } - - return switch (fstatat(dir_fd, subpath)) { - .err => |err| switch (err.getErrno()) { - .NOENT => .{ .result = false }, - else => .{ .err = err }, - }, - .result => |result| .{ .result = S.ISDIR(result.mode) }, + .{ .err = err }, + .result => |result| .{ .result = result == .directory }, }; } @@ -3331,15 +3263,19 @@ pub fn updateNonblocking(fd: bun.FileDescriptor, nonblocking: bool) Maybe(void) return Maybe(void).success; } -pub fn existsAt(fd: bun.FileDescriptor, subpath: [:0]const u8) bool { - if (comptime Environment.isPosix) { - return faccessat(fd, subpath).result; - } - +pub const ExistsAtType = enum { + file, + directory, +}; +pub fn existsAtType(fd: bun.FileDescriptor, subpath: anytype) Maybe(ExistsAtType) { if (comptime Environment.isWindows) { const wbuf = bun.WPathBufferPool.get(); defer bun.WPathBufferPool.put(wbuf); - const path = bun.strings.toNTPath(wbuf, subpath); + const path = if (std.meta.Child(@TypeOf(subpath)) == u16) + bun.strings.toNTPath16(wbuf, subpath) + else + bun.strings.toNTPath(wbuf, subpath); + const path_len_bytes: u16 = @truncate(path.len * 2); var nt_name = w.UNICODE_STRING{ .Length = path_len_bytes, @@ -3361,9 +3297,9 @@ pub fn existsAt(fd: bun.FileDescriptor, subpath: [:0]const u8) bool { }; var basic_info: w.FILE_BASIC_INFORMATION = undefined; const rc = kernel32.NtQueryAttributesFile(&attr, &basic_info); - if (JSC.Maybe(bool).errnoSysP(rc, .access, subpath)) |err| { + if (JSC.Maybe(bool).errnoSys(rc, .access)) |err| { syslog("NtQueryAttributesFile({}, O_RDONLY, 0) = {}", .{ bun.fmt.fmtOSPath(path, .{}), err }); - return false; + return .{ .err = err.err }; } const is_regular_file = basic_info.FileAttributes != kernel32.INVALID_FILE_ATTRIBUTES and @@ -3371,9 +3307,51 @@ pub fn existsAt(fd: bun.FileDescriptor, subpath: [:0]const u8) bool { // https://github.com/libuv/libuv/blob/eb5af8e3c0ea19a6b0196d5db3212dae1785739b/src/win/fs.c#L2144-L2146 (basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_DIRECTORY == 0 or basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_READONLY == 0); - syslog("NtQueryAttributesFile({}, O_RDONLY, 0) = {d}", .{ bun.fmt.fmtOSPath(path, .{}), @intFromBool(is_regular_file) }); - return is_regular_file; + const is_dir = basic_info.FileAttributes != kernel32.INVALID_FILE_ATTRIBUTES and + basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_DIRECTORY != 0 and + basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_READONLY == 0; + + return if (is_dir) { + syslog("NtQueryAttributesFile({}, O_RDONLY, 0) = directory", .{bun.fmt.fmtOSPath(path, .{})}); + return .{ .result = .directory }; + } else if (is_regular_file) { + syslog("NtQueryAttributesFile({}, O_RDONLY, 0) = file", .{bun.fmt.fmtOSPath(path, .{})}); + return .{ .result = .file }; + } else { + syslog("NtQueryAttributesFile({}, O_RDONLY, 0) = {d}", .{ bun.fmt.fmtOSPath(path, .{}), basic_info.FileAttributes }); + return .{ .err = bun.sys.Error.fromCode(.UNKNOWN, .access) }; + }; + } + + if (std.meta.sentinel(@TypeOf(subpath)) == null) { + const path_buf = bun.PathBufferPool.get(); + defer bun.PathBufferPool.put(path_buf); + @memcpy(path_buf, subpath); + path_buf[subpath.len] = 0; + const slice: [:0]const u8 = @ptrCast(path_buf); + return existsAtType(fd, slice); + } + + return switch (fstatat(fd, subpath)) { + .err => |err| .{ .err = err }, + .result => |result| if (S.ISDIR(result.mode)) .{ .result = .directory } else .{ .result = .file }, + }; +} + +pub fn existsAt(fd: bun.FileDescriptor, subpath: [:0]const u8) bool { + if (comptime Environment.isPosix) { + return switch (faccessat(fd, subpath)) { + .err => false, + .result => |r| r, + }; + } + + if (comptime Environment.isWindows) { + if (existsAtType(fd, subpath).asValue()) |exists_at_type| { + return exists_at_type == .file; + } + return false; } @compileError("TODO: existsAtOSPath"); diff --git a/test/cli/create/__snapshots__/create-jsx.test.ts.snap b/test/cli/create/__snapshots__/create-jsx.test.ts.snap new file mode 100644 index 00000000000000..56b869570e3b88 --- /dev/null +++ b/test/cli/create/__snapshots__/create-jsx.test.ts.snap @@ -0,0 +1,231 @@ +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`development: true react spa (no tailwind) dev server 1`] = ` +" + + +index | Powered by Bun + + +

Welcome to Bun

The all-in-one JavaScript runtime & toolkit designed for speed

3xBun Bun Bun
0.5sAverage Install Time
ExtremelyNode.js Compatible

Why Choose Bun?

⚡️

Lightning Fast

Built from scratch in Zig, Bun is focused on performance and developer experience

🎯

All-in-One

Bundler, test runner, and npm-compatible package manager in a single tool

🚀

JavaScript Runtime

Drop-in replacement for Node.js with 3x faster startup time

📦

Package Management

Native package manager that can install dependencies up to 30x faster than npm

🧪

Testing Made Simple

Built-in test runner with Jest-compatible API and snapshot testing

🔥

Hot Reloading

Lightning-fast hot module replacement (HMR) for rapid development

+ +" +`; + +exports[`development: true react spa (no tailwind) dev server 2`] = ` +"create index.build.ts build +create index.css css +create index.html html +create index.client.tsx bun +create package.json npm +📦 Auto-installing 3 detected dependencies +$ bun --only-missing install classnames react-dom@19 react@19 +bun add v*.*.* +installed classnames@*.*.* +installed react-dom@*.*.* +installed react@*.*.* +4 packages installed [*ms] +-------------------------------- +✨ React project configured +Development - frontend dev server with hot reload +bun dev +Production - build optimized assets +bun run build +Happy bunning! 🐇 +Bun v*.*.* dev server ready in *.** ms +url: http://[SERVER_URL]/" +`; + +exports[`development: true react spa (tailwind) dev server 1`] = ` +" + + +index | Powered by Bun + + +

bun create for React

Start a React dev server instantly from a single component file

bun create ./MyComponent.tsx

Zero Config

Just write your React component and run. No setup needed.

Auto Dependencies

Automatically detects and installs required npm packages.

Tool Detection

Recognizes Tailwind, animations, and UI libraries automatically.

How it Works

1

Create Component

Write your React component in a .tsx file

2

Run Command

Execute bun create with your file path

3

Start Developing

Dev server starts instantly with hot reload

Ready to Try?

+ +" +`; + +exports[`development: true react spa (tailwind) dev server 2`] = ` +"create index.build.ts build +create index.css css +create index.html html +create index.client.tsx bun +create bunfig.toml bun +create package.json npm +📦 Auto-installing 4 detected dependencies +$ bun --only-missing install tailwindcss bun-plugin-tailwind react-dom@19 react@19 +bun add v*.*.* +installed tailwindcss@*.*.* +installed bun-plugin-tailwind@*.*.* +installed react-dom@*.*.* +installed react@*.*.* +7 packages installed [*ms] +-------------------------------- +✨ React + Tailwind project configured +Development - frontend dev server with hot reload +bun dev +Production - build optimized assets +bun run build +Happy bunning! 🐇 +Bun v*.*.* dev server ready in *.** ms +url: http://[SERVER_URL]/" +`; + +exports[`development: true shadcn/ui dev server 1`] = ` +"create lib/utils.ts shadcn +create src/index.css shadcn +create index.build.ts bun +create index.client.tsx bun +create index.css css +create index.html html +create styles/globals.css shadcn +create bunfig.toml bun +create package.json npm +create tsconfig.json tsc +create components.json shadcn +📦 Auto-installing 9 detected dependencies +$ bun --only-missing install lucide-react tailwindcss bun-plugin-tailwind tailwindcss-animate class-variance-authority clsx tailwind-merge react@^18 react-dom@^18 +bun add v*.*.* +installed lucide-react@*.*.* +installed tailwindcss@*.*.* +installed bun-plugin-tailwind@*.*.* +installed tailwindcss-animate@*.*.* +installed class-variance-authority@*.*.* +installed clsx@*.*.* +installed tailwind-merge@*.*.* +installed react@*.*.* +installed react-dom@*.*.* +14 packages installed [*ms] +😎 Setting up shadcn/ui components +$ bun x shadcn@canary add -y button badge card +- components/ui/button.tsx +- components/ui/badge.tsx +- components/ui/card.tsx +-------------------------------- +✨ React + shadcn/ui + Tailwind project configured +Development - frontend dev server with hot reload +bun dev +Production - build optimized assets +bun run build +Happy bunning! 🐇 +Bun v*.*.* dev server ready in *.** ms +url: http://[SERVER_URL]/" +`; + +exports[`development: false react spa (no tailwind) dev server 1`] = ` +" + + +index | Powered by Bun + + + +

Welcome to Bun

The all-in-one JavaScript runtime & toolkit designed for speed

3xBun Bun Bun
0.5sAverage Install Time
ExtremelyNode.js Compatible

Why Choose Bun?

⚡️

Lightning Fast

Built from scratch in Zig, Bun is focused on performance and developer experience

🎯

All-in-One

Bundler, test runner, and npm-compatible package manager in a single tool

🚀

JavaScript Runtime

Drop-in replacement for Node.js with 3x faster startup time

📦

Package Management

Native package manager that can install dependencies up to 30x faster than npm

🧪

Testing Made Simple

Built-in test runner with Jest-compatible API and snapshot testing

🔥

Hot Reloading

Lightning-fast hot module replacement (HMR) for rapid development

+" +`; + +exports[`development: false react spa (no tailwind) dev server 2`] = ` +"create index.build.ts build +create index.css css +create index.html html +create index.client.tsx bun +create package.json npm +📦 Auto-installing 3 detected dependencies +$ bun --only-missing install classnames react-dom@19 react@19 +bun add v*.*.* +installed classnames@*.*.* +installed react-dom@*.*.* +installed react@*.*.* +4 packages installed [*ms] +-------------------------------- +✨ React project configured +Development - frontend dev server with hot reload +bun dev +Production - build optimized assets +bun run build +Happy bunning! 🐇 +Bun v*.*.* ready in *.** ms +url: http://[SERVER_URL]/" +`; + +exports[`development: false react spa (tailwind) dev server 1`] = ` +" + + +index | Powered by Bun + + + +

bun create for React

Start a React dev server instantly from a single component file

bun create ./MyComponent.tsx

Zero Config

Just write your React component and run. No setup needed.

Auto Dependencies

Automatically detects and installs required npm packages.

Tool Detection

Recognizes Tailwind, animations, and UI libraries automatically.

How it Works

1

Create Component

Write your React component in a .tsx file

2

Run Command

Execute bun create with your file path

3

Start Developing

Dev server starts instantly with hot reload

Ready to Try?

+" +`; + +exports[`development: false react spa (tailwind) dev server 2`] = ` +"create index.build.ts build +create index.css css +create index.html html +create index.client.tsx bun +create bunfig.toml bun +create package.json npm +📦 Auto-installing 4 detected dependencies +$ bun --only-missing install tailwindcss bun-plugin-tailwind react-dom@19 react@19 +bun add v*.*.* +installed tailwindcss@*.*.* +installed bun-plugin-tailwind@*.*.* +installed react-dom@*.*.* +installed react@*.*.* +7 packages installed [*ms] +-------------------------------- +✨ React + Tailwind project configured +Development - frontend dev server with hot reload +bun dev +Production - build optimized assets +bun run build +Happy bunning! 🐇 +Bun v*.*.* ready in *.** ms +url: http://[SERVER_URL]/" +`; + +exports[`development: false shadcn/ui dev server 1`] = ` +"create lib/utils.ts shadcn +create src/index.css shadcn +create index.build.ts bun +create index.client.tsx bun +create index.css css +create index.html html +create styles/globals.css shadcn +create bunfig.toml bun +create package.json npm +create tsconfig.json tsc +create components.json shadcn +📦 Auto-installing 9 detected dependencies +$ bun --only-missing install lucide-react tailwindcss bun-plugin-tailwind tailwindcss-animate class-variance-authority clsx tailwind-merge react@^18 react-dom@^18 +bun add v*.*.* +installed lucide-react@*.*.* +installed tailwindcss@*.*.* +installed bun-plugin-tailwind@*.*.* +installed tailwindcss-animate@*.*.* +installed class-variance-authority@*.*.* +installed clsx@*.*.* +installed tailwind-merge@*.*.* +installed react@*.*.* +installed react-dom@*.*.* +14 packages installed [*ms] +😎 Setting up shadcn/ui components +$ bun x shadcn@canary add -y button badge card +- components/ui/button.tsx +- components/ui/badge.tsx +- components/ui/card.tsx +-------------------------------- +✨ React + shadcn/ui + Tailwind project configured +Development - frontend dev server with hot reload +bun dev +Production - build optimized assets +bun run build +Happy bunning! 🐇 +Bun v*.*.* ready in *.** ms +url: http://[SERVER_URL]/" +`; diff --git a/test/cli/create/create-jsx.test.ts b/test/cli/create/create-jsx.test.ts new file mode 100644 index 00000000000000..e6c76645fccb5d --- /dev/null +++ b/test/cli/create/create-jsx.test.ts @@ -0,0 +1,366 @@ +import "bun"; +import { expect, test, describe, beforeEach, afterAll } from "bun:test"; +import { tempDirWithFiles as tempDir, bunExe, bunEnv, isCI, isWindows } from "harness"; +import { cp, readdir } from "fs/promises"; +import path from "path"; +import puppeteer, { type Browser } from "puppeteer"; +import type { Subprocess } from "bun"; +import * as vm from "vm"; +const env = { + ...bunEnv, +}; +const baseOptions = { + dumpio: !!process.env.CI_DEBUG, + + args: [ + "--disable-gpu", + "--disable-dev-shm-usage", + "--disable-setuid-sandbox", + "--no-sandbox", + "--ignore-certificate-errors", + "--use-fake-ui-for-media-stream", + "--use-fake-device-for-media-stream", + "--disable-sync", + ], + executablePath: process.env.BROWSER_EXECUTABLE, + headless: true, +}; +let puppeteerBrowser: Browser | null = null; +async function getPuppeteerBrowser() { + if (!puppeteerBrowser) { + puppeteerBrowser = await puppeteer.launch(baseOptions); + } + return puppeteerBrowser; +} + +afterAll(async () => { + if (puppeteerBrowser) { + await puppeteerBrowser.close(); + } +}); + +async function getServerUrl(process: Subprocess, all = { text: "" }) { + // Read the port number from stdout + const decoder = new TextDecoder(); + let serverUrl = ""; + all.text = ""; + + const reader = process.stdout.getReader(); + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const textChunk = decoder.decode(value, { stream: true }); + all.text += textChunk; + console.log(textChunk); + + if (all.text.includes("http://")) { + serverUrl = all.text.trim(); + serverUrl = serverUrl.slice(serverUrl.indexOf("http://")); + + serverUrl = serverUrl.slice(0, serverUrl.indexOf("\n")); + if (URL.canParse(serverUrl)) { + break; + } + + serverUrl = serverUrl.slice(0, serverUrl.indexOf("/n")); + serverUrl = serverUrl.slice(0, serverUrl.lastIndexOf("/")); + serverUrl = serverUrl.trim(); + + if (URL.canParse(serverUrl)) { + break; + } + } + } + reader.releaseLock(); + + if (!serverUrl) { + throw new Error("Could not find server URL in stdout"); + } + + return serverUrl; +} + +async function checkBuildOutput(dir: string) { + const distDir = path.join(dir, "dist"); + const files = await readdir(distDir); + expect(files.some(f => f.endsWith(".js"))).toBe(true); + expect(files.some(f => f.endsWith(".html"))).toBe(true); + expect(files.some(f => f.endsWith(".css"))).toBe(true); +} + +describe.each(["true", "false"])("development: %s", developmentString => { + const development = developmentString === "true"; + const tempDirWithFiles = (name: string, files: Record) => + tempDir(name + (development ? "-dev" : "-prod"), files); + const normalizeHTML = normalizeHTMLFn(development); + const env = { + ...bunEnv, + NODE_PORT: "0", + NODE_ENV: development ? undefined : "production", + }; + + const devServerLabel = development ? " dev server" : ""; + describe("react spa (no tailwind)", async () => { + let dir: string; + beforeEach(async () => { + dir = tempDirWithFiles("react-spa-no-tailwind", { + "README.md": "Hello, world!", + }); + + await cp(path.join(__dirname, "react-spa-no-tailwind"), dir, { + recursive: true, + force: true, + }); + }); + + test.todoIf(isCI)("dev server", async () => { + await using process = Bun.spawn([bunExe(), "create", "./index.jsx"], { + cwd: dir, + env: env, + stdout: "pipe", + stdin: "ignore", + }); + const all = { text: "" }; + const serverUrl = await getServerUrl(process, all); + + try { + const browser = await getPuppeteerBrowser(); + var page = await browser.newPage(); + await page.goto(serverUrl, { waitUntil: "networkidle0" }); + + const content = await page.evaluate(() => document.documentElement.innerHTML); + + expect(normalizeHTML(content)).toMatchSnapshot(); + + expect( + all.text + .replace(/v\d+\.\d+\.\d+(?:\s*\([a-f0-9]+\))?/g, "v*.*.*") // Handle version with git hash + .replace(/\[\d+\.?\d*m?s\]/g, "[*ms]") + .replace(/@\d+\.\d+\.\d+/g, "@*.*.*") + .replace(/\d+\.\d+\s*ms/g, "*.** ms") + .replace(/^\s+/gm, "") // Remove leading spaces + .replace(/installed react(-dom)?@\d+\.\d+\.\d+/g, "installed react$1@*.*.*") // Handle react versions + .trim() + .replaceAll(serverUrl, "http://[SERVER_URL]"), + ).toMatchSnapshot(); + } finally { + process.kill(); + await Promise.resolve(page!?.close?.({ runBeforeUnload: false })); + } + }); + + test.todoIf(isWindows)("build", async () => { + { + const process = Bun.spawn([bunExe(), "create", "./index.jsx"], { + cwd: dir, + env: env, + stdout: "pipe", + stdin: "ignore", + }); + const all = { text: "" }; + const serverUrl = await getServerUrl(process, all); + process.kill(); + } + + const process = Bun.spawn([bunExe(), "run", "build"], { + cwd: dir, + env: env, + stdout: "pipe", + }); + + await process.exited; + await checkBuildOutput(dir); + }); + }); + + describe("react spa (tailwind)", async () => { + let dir: string; + beforeEach(async () => { + dir = tempDirWithFiles("react-spa-tailwind", { + "index.tsx": await Bun.file(path.join(__dirname, "tailwind.tsx")).text(), + }); + }); + + test.todoIf(isCI)("dev server", async () => { + const process = Bun.spawn([bunExe(), "create", "./index.tsx"], { + cwd: dir, + env: env, + stdout: "pipe", + stdin: "ignore", + }); + const all = { text: "" }; + const serverUrl = await getServerUrl(process, all); + console.log(serverUrl); + + try { + var page = await (await getPuppeteerBrowser()).newPage(); + await page.goto(serverUrl, { waitUntil: "networkidle0" }); + + // Check that React root exists and has Tailwind classes + const root = await page.$("#root"); + expect(root).toBeTruthy(); + + const content = await page.evaluate(() => document.documentElement.outerHTML); + expect(normalizeHTML(content)).toMatchSnapshot(); + + expect( + all.text + .replace(/v\d+\.\d+\.\d+(?:\s*\([a-f0-9]+\))?/g, "v*.*.*") + .replace(/\[\d+\.?\d*m?s\]/g, "[*ms]") + .replace(/@\d+\.\d+\.\d+/g, "@*.*.*") + .replace(/\d+\.\d+\s*ms/g, "*.** ms") + .replace(/^\s+/gm, "") + .replace(/installed (react(-dom)?|tailwindcss)@\d+\.\d+\.\d+/g, "installed $1@*.*.*") + .trim() + .replaceAll(serverUrl, "http://[SERVER_URL]"), + ).toMatchSnapshot(); + } finally { + process.kill(); + await Promise.resolve(page!?.close?.({ runBeforeUnload: false })); + } + }); + + test.todoIf(isWindows)("build", async () => { + { + const process = Bun.spawn([bunExe(), "create", "./index.tsx"], { + cwd: dir, + env: env, + stdout: "pipe", + stdin: "ignore", + }); + const all = { text: "" }; + const serverUrl = await getServerUrl(process, all); + process.kill(); + } + + const process = Bun.spawn([bunExe(), "run", "build"], { + cwd: dir, + env: env, + stdout: "pipe", + }); + + await process.exited; + await checkBuildOutput(dir); + }); + }); + + describe( + "shadcn/ui", + async () => { + let dir: string; + beforeEach(async () => { + dir = tempDirWithFiles("shadcn-ui", { + "index.tsx": await Bun.file(path.join(__dirname, "shadcn.tsx")).text(), + }); + }); + + test( + "dev server", + async () => { + const process = Bun.spawn([bunExe(), "create", "./index.tsx"], { + cwd: dir, + env: env, + stdout: "pipe", + stdin: "ignore", + }); + const all = { text: "" }; + const serverUrl = await getServerUrl(process, all); + console.log(serverUrl); + console.log(dir); + try { + var page = await (await getPuppeteerBrowser()).newPage(); + await page.goto(serverUrl, { waitUntil: "networkidle0" }); + + // Check that React root exists and has Shadcn components + const root = await page.$("#root"); + expect(root).toBeTruthy(); + + const content = await page.evaluate(() => document.documentElement.innerHTML); + expect(content).toContain("shadcn"); // Basic check for Shadcn classes + + // Check for components.json + const componentsJson = await Bun.file(path.join(dir, "components.json")).exists(); + expect(componentsJson).toBe(true); + + expect( + all.text + .replace(/v\d+\.\d+\.\d+(?:\s*\([a-f0-9]+\))?/g, "v*.*.*") + .replace(/\[\d+\.?\d*m?s\]/g, "[*ms]") + .replace(/@\d+\.\d+\.\d+/g, "@*.*.*") + .replace(/\d+\.\d+\s*ms/g, "*.** ms") + .replace(/^\s+/gm, "") + .replace( + /installed (react(-dom)?|@radix-ui\/.*|tailwindcss|class-variance-authority|clsx|lucide-react|tailwind-merge)@\d+\.\d+\.\d+/g, + "installed $1@*.*.*", + ) + .trim() + .replaceAll(serverUrl, "http://[SERVER_URL]"), + ).toMatchSnapshot(); + } finally { + process.kill(); + await Promise.resolve(page!?.close?.({ runBeforeUnload: false })); + } + }, + 1000 * 100, + ); + + test.todoIf(isWindows)("build", async () => { + { + const process = Bun.spawn([bunExe(), "create", "./index.tsx"], { + cwd: dir, + env: env, + stdout: "pipe", + stdin: "ignore", + }); + const all = { text: "" }; + const serverUrl = await getServerUrl(process, all); + process.kill(); + } + + const process = Bun.spawn([bunExe(), "run", "build"], { + cwd: dir, + env: env, + stdout: "pipe", + }); + + await process.exited; + await checkBuildOutput(dir); + }); + }, + 1000 * 100, + ); +}); + +function normalizeHTMLFn(development: boolean = true) { + return (html: string) => + html + .split("\n") + .map(line => { + // First trim the line + const trimmed = line.trim(); + if (!trimmed) return ""; + + if (!development) { + // Replace chunk hashes in stylesheet and script tags + return trimmed.replace( + /<(link rel="stylesheet" crossorigin="" href|script type="module" crossorigin="" src)="\/chunk-[a-zA-Z0-9]+\.(css|js)("><\/script>|">)/g, + (_, tagStart, ext) => { + if (ext === "css") { + return `<${tagStart}="/chunk-[HASH].css">`; + } + return `<${tagStart}="/chunk-[HASH].js">`; + }, + ); + } + + // In development mode, replace generational IDs in script/link tags + return trimmed.replace( + /<(link rel="stylesheet" href|script type="module" src)="\/_bun\/(client|asset)\/[^"]+\.(?:css|js)("><\/script>|">)/g, + (_, tagStart, path, end) => `<${tagStart}="/_bun/${path}/[GENERATION_ID]${end}`, + ); + }) + .filter(Boolean) + .join("\n") + .trim(); +} diff --git a/test/cli/create/react-spa-no-tailwind/components/Feature.jsx b/test/cli/create/react-spa-no-tailwind/components/Feature.jsx new file mode 100644 index 00000000000000..0d1cd5fa9e2a60 --- /dev/null +++ b/test/cli/create/react-spa-no-tailwind/components/Feature.jsx @@ -0,0 +1,27 @@ +import React from "react"; +import classNames from "classnames"; + +export default function Feature({ icon, title, description, highlight }) { + return ( +
+
{icon}
+

{title}

+

+ {highlight ? ( + <> + {description.split(highlight).map((part, i, arr) => ( + + {part} + {i < arr.length - 1 && ( + {highlight} + )} + + ))} + + ) : ( + description + )} +

+
+ ); +} diff --git a/test/cli/create/react-spa-no-tailwind/components/Features.jsx b/test/cli/create/react-spa-no-tailwind/components/Features.jsx new file mode 100644 index 00000000000000..3b5e492ae58b29 --- /dev/null +++ b/test/cli/create/react-spa-no-tailwind/components/Features.jsx @@ -0,0 +1,56 @@ +import React from "react"; +import Feature from "./Feature"; + +const FEATURES = [ + { + icon: "⚡️", + title: "Lightning Fast", + description: + "Built from scratch in Zig, Bun is focused on performance and developer experience", + highlight: "Zig", + }, + { + icon: "🎯", + title: "All-in-One", + description: + "Bundler, test runner, and npm-compatible package manager in a single tool", + }, + { + icon: "🚀", + title: "JavaScript Runtime", + description: "Drop-in replacement for Node.js with 3x faster startup time", + highlight: "3x faster", + }, + { + icon: "📦", + title: "Package Management", + description: + "Native package manager that can install dependencies up to 30x faster than npm", + highlight: "30x faster", + }, + { + icon: "🧪", + title: "Testing Made Simple", + description: + "Built-in test runner with Jest-compatible API and snapshot testing", + }, + { + icon: "🔥", + title: "Hot Reloading", + description: + "Lightning-fast hot module replacement (HMR) for rapid development", + }, +]; + +export default function Features() { + return ( +
+

Why Choose Bun?

+
+ {FEATURES.map((feature, index) => ( + + ))} +
+
+ ); +} diff --git a/test/cli/create/react-spa-no-tailwind/components/Footer.jsx b/test/cli/create/react-spa-no-tailwind/components/Footer.jsx new file mode 100644 index 00000000000000..20d63f7966f4ea --- /dev/null +++ b/test/cli/create/react-spa-no-tailwind/components/Footer.jsx @@ -0,0 +1,35 @@ +import React from "react"; +import classNames from "classnames"; + +const LINKS = [ + { text: "Documentation", url: "https://bun.sh/docs" }, + { text: "GitHub", url: "https://github.com/oven-sh/bun" }, + { text: "Discord", url: "https://bun.sh/discord" }, + { text: "Blog", url: "https://bun.sh/blog" }, +]; + +export default function Footer() { + return ( +
+
+
+ 🥟 + Built with Bun +
+ +
+
+ ); +} diff --git a/test/cli/create/react-spa-no-tailwind/components/Hero.jsx b/test/cli/create/react-spa-no-tailwind/components/Hero.jsx new file mode 100644 index 00000000000000..27bd415b7dca08 --- /dev/null +++ b/test/cli/create/react-spa-no-tailwind/components/Hero.jsx @@ -0,0 +1,48 @@ +import React from "react"; +import classNames from "classnames"; + +export default function Hero() { + return ( +
+
🥟
+

+ Welcome to Bun +

+

+ The all-in-one JavaScript runtime & toolkit designed for speed +

+ +
+
+ 3x + Bun Bun Bun +
+
+ 0.5s + Average Install Time +
+
+ Extremely + Node.js Compatible +
+
+
+ ); +} diff --git a/test/cli/create/react-spa-no-tailwind/index.html b/test/cli/create/react-spa-no-tailwind/index.html new file mode 100644 index 00000000000000..78c0dcd8701944 --- /dev/null +++ b/test/cli/create/react-spa-no-tailwind/index.html @@ -0,0 +1,12 @@ + + + + + + Bun - The Modern JavaScript Runtime + + +
+ + + diff --git a/test/cli/create/react-spa-no-tailwind/index.jsx b/test/cli/create/react-spa-no-tailwind/index.jsx new file mode 100644 index 00000000000000..7f38d302b0cf14 --- /dev/null +++ b/test/cli/create/react-spa-no-tailwind/index.jsx @@ -0,0 +1,22 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; +import classNames from "classnames"; +import "./styles.css"; + +import Hero from "./components/Hero"; +import Features from "./components/Features"; +import Footer from "./components/Footer"; + +function App() { + return ( +
+
+ + +
+
+
+ ); +} + +export default App; diff --git a/test/cli/create/react-spa-no-tailwind/styles.css b/test/cli/create/react-spa-no-tailwind/styles.css new file mode 100644 index 00000000000000..8a143902f0dc6d --- /dev/null +++ b/test/cli/create/react-spa-no-tailwind/styles.css @@ -0,0 +1,278 @@ +:root { + --primary-color: #fbf0ff; + --accent-color: #7c3aed; + --text-color: #1a1a1a; + --secondary-color: #4c1d95; + --gray-100: #f3f4f6; + --gray-200: #e5e7eb; + --gray-700: #374151; + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, + Ubuntu, Cantarell, sans-serif; + background: var(--primary-color); + color: var(--text-color); + line-height: 1.6; +} + +.app { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; + flex: 1; +} + +/* Hero Section */ +.hero { + margin: 4rem 0; + text-align: center; +} + +.logo { + font-size: 5rem; + margin-bottom: 1rem; + display: inline-block; +} + +.animate-bounce { + animation: bounce 2s infinite; +} + +@keyframes bounce { + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-20px); + } +} + +h1 { + font-size: 3.5rem; + margin-bottom: 1rem; + background: linear-gradient( + 120deg, + var(--accent-color), + var(--secondary-color) + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.gradient-text { + background: linear-gradient( + 120deg, + var(--accent-color), + var(--secondary-color) + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.description { + font-size: 1.5rem; + margin-bottom: 2rem; + color: var(--gray-700); + max-width: 600px; + margin-left: auto; + margin-right: auto; +} + +/* CTA Buttons */ +.cta-buttons { + display: flex; + gap: 1rem; + justify-content: center; + margin-bottom: 3rem; +} + +.button { + padding: 0.75rem 1.5rem; + border-radius: 8px; + font-weight: 600; + text-decoration: none; + transition: transform 0.2s, box-shadow 0.2s; +} + +.button:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.button.primary { + background: var(--accent-color); + color: white; +} + +.button.secondary { + background: white; + color: var(--accent-color); + border: 2px solid var(--accent-color); +} + +/* Stats */ +.stats { + display: flex; + justify-content: center; + gap: 3rem; + margin-top: 3rem; +} + +.stat { + text-align: center; +} + +.stat-value { + font-size: 2.5rem; + font-weight: bold; + color: var(--accent-color); + display: block; +} + +.stat-label { + color: var(--gray-700); + font-size: 1rem; +} + +/* Features Section */ +.features-section { + padding: 4rem 0; +} + +.features-section h2 { + text-align: center; + font-size: 2.5rem; + margin-bottom: 3rem; + color: var(--accent-color); +} + +.features { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; +} + +.feature { + padding: 2rem; + background: white; + border-radius: 12px; + box-shadow: var(--shadow-sm); + transition: all 0.3s ease; +} + +.feature:hover { + transform: translateY(-5px); + box-shadow: var(--shadow-md); +} + +.feature-icon { + font-size: 2.5rem; + margin-bottom: 1rem; +} + +.feature h3 { + color: var(--accent-color); + margin-bottom: 1rem; + font-size: 1.5rem; +} + +.highlight { + background: linear-gradient(120deg, #7c3aed20 0%, #7c3aed10 100%); + padding: 0.2em 0.4em; + border-radius: 4px; + font-weight: bold; +} + +/* Footer */ +.footer { + background: white; + padding: 2rem 0; + margin-top: 4rem; + border-top: 1px solid var(--gray-200); +} + +.footer-content { + max-width: 1200px; + margin: 0 auto; + padding: 0 2rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.footer-logo { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.logo-small { + font-size: 1.5rem; +} + +.footer-text { + font-weight: 500; + color: var(--gray-700); +} + +.footer-links { + display: flex; + gap: 2rem; +} + +.footer-link { + color: var(--gray-700); + text-decoration: none; + transition: color 0.2s; +} + +.footer-link:hover { + color: var(--accent-color); +} + +/* Responsive Design */ +@media (max-width: 768px) { + .container { + padding: 1rem; + } + + h1 { + font-size: 2.5rem; + } + + .description { + font-size: 1.2rem; + } + + .stats { + flex-direction: column; + gap: 2rem; + } + + .footer-content { + flex-direction: column; + gap: 1rem; + text-align: center; + } + + .footer-links { + flex-wrap: wrap; + justify-content: center; + } +} diff --git a/test/cli/create/shadcn.tsx b/test/cli/create/shadcn.tsx new file mode 100644 index 00000000000000..71eb1f1faae060 --- /dev/null +++ b/test/cli/create/shadcn.tsx @@ -0,0 +1,105 @@ +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Card } from "@/components/ui/card"; +import { CheckCircle } from "lucide-react"; + +export default function LandingPage() { + const features = [ + { + title: "Auto Dependencies", + description: + "Automatically detects and installs required dependencies for your component", + }, + { + title: "Tool Detection", + description: + "Seamlessly integrates with Tailwind CSS, shadcn/ui, and other popular tools", + }, + { + title: "Zero Config", + description: + "No setup required. Start developing instantly with hot reload enabled", + }, + ]; + + return ( +
+
+ {/* Hero Section */} +
+ + New in Bun 1.2.3 + +

+ From Component to App in + Seconds +

+

+ Start a complete dev server from a single React component. No config + needed. +

+ +
+ + +
+
+ + {/* Code Preview */} + +
+
+              
+                $ bun create ./MyComponent.tsx
+                
+ 📦 Installing dependencies... +
+ 🔍 Detected Tailwind CSS +
+ 🎨 Detected shadcn/ui +
✨ Dev server running at http://localhost:3000 +
+
+
+
+ + {/* Features Grid */} +
+ {features.map((feature, index) => ( + +
+ +

{feature.title}

+
+

{feature.description}

+
+ ))} +
+ + {/* CTA Section */} +
+ +

+ Ready to streamline your React development? +

+

+ Get started with Bun's powerful component development workflow + today. +

+ +
+
+
+
+ ); +} + diff --git a/test/cli/create/tailwind.tsx b/test/cli/create/tailwind.tsx new file mode 100644 index 00000000000000..ca2a550b2ffa1d --- /dev/null +++ b/test/cli/create/tailwind.tsx @@ -0,0 +1,88 @@ +export default function LandingPage() { + let copied = false; + const handleCopy = () => { + navigator.clipboard.writeText("bun create ./MyComponent.tsx"); + }; + + return ( +
+
+
+

+ bun create for React +

+

Start a React dev server instantly from a single component file

+ +
+ bun create ./MyComponent.tsx + +
+
+ +
+
+

Zero Config

+

Just write your React component and run. No setup needed.

+
+ +
+

Auto Dependencies

+

Automatically detects and installs required npm packages.

+
+ +
+

Tool Detection

+

Recognizes Tailwind, animations, and UI libraries automatically.

+
+
+ +
+

How it Works

+
+
+
1
+
+

Create Component

+

Write your React component in a .tsx file

+
+
+
+
2
+
+

Run Command

+

Execute bun create with your file path

+
+
+
+
3
+
+

Start Developing

+

Dev server starts instantly with hot reload

+
+
+
+
+ +
+

Ready to Try?

+ +
+
+
+ ); +} diff --git a/test/cli/install/bun-add.test.ts b/test/cli/install/bun-add.test.ts index d2deadd510e6cf..c8ef1e25912de9 100644 --- a/test/cli/install/bun-add.test.ts +++ b/test/cli/install/bun-add.test.ts @@ -125,6 +125,151 @@ it("should reject missing package", async () => { ); }); +it("bun add --only-missing should not install existing package", async () => { + const urls: string[] = []; + setHandler(dummyRegistry(urls)); + await writeFile( + join(package_dir, "package.json"), + JSON.stringify({ + name: "foo", + version: "0.0.1", + }), + ); + + // First time: install succesfully. + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "add", "--only-missing", "bar"], + cwd: package_dir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + }); + const err = await new Response(stderr).text(); + expect(err).not.toContain("error:"); + expect(err).toContain("Saved lockfile"); + const out = await new Response(stdout).text(); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + expect.stringContaining("bun add v1."), + "", + "installed bar@0.0.2", + "", + "1 package installed", + ]); + expect(await exited).toBe(0); + expect(urls.sort()).toEqual([`${root_url}/bar`, `${root_url}/bar-0.0.2.tgz`]); + expect(requested).toBe(2); + expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".cache", "bar"]); + expect(await readdirSorted(join(package_dir, "node_modules", "bar"))).toEqual(["package.json"]); + expect(await file(join(package_dir, "node_modules", "bar", "package.json")).json()).toEqual({ + name: "bar", + version: "0.0.2", + }); + expect(await file(join(package_dir, "package.json")).text()).toEqual( + JSON.stringify( + { + name: "foo", + version: "0.0.1", + dependencies: { + bar: "^0.0.2", + }, + }, + null, + 2, + ), + ); + await access(join(package_dir, "bun.lockb")); + + { + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "add", "bar", "--only-missing"], + cwd: package_dir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + }); + const out = await new Response(stdout).text(); + expect(out).not.toContain("Saved lockfile"); + expect(out).not.toContain("Installed"); + expect(out.split("\n").filter(Boolean)).toStrictEqual([ + expect.stringContaining("bun add v" + Bun.version.replaceAll("-debug", "")), + ]); + } +}); + +it("bun add --analyze should scan dependencies", async () => { + const urls: string[] = []; + setHandler(dummyRegistry(urls)); + await writeFile( + join(package_dir, "package.json"), + JSON.stringify({ + name: "foo", + version: "0.0.1", + }), + ); + await writeFile(join(package_dir, "entry-point.ts"), `import "./local-file.ts";`); + await writeFile(join(package_dir, "local-file.ts"), `export * from "bar";`); + console.log(package_dir); + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "add", "./entry-point.ts", "--analyze"], + cwd: package_dir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + env, + }); + const err = await new Response(stderr).text(); + expect(err).not.toContain("error:"); + expect(err).toContain("Saved lockfile"); + const out = await new Response(stdout).text(); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + expect.stringContaining("bun add v1."), + "", + "installed bar@0.0.2", + "", + "1 package installed", + ]); + expect(await exited).toBe(0); + expect(urls.sort()).toEqual([`${root_url}/bar`, `${root_url}/bar-0.0.2.tgz`]); + expect(requested).toBe(2); + expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".cache", "bar"]); + expect(await readdirSorted(join(package_dir, "node_modules", "bar"))).toEqual(["package.json"]); + expect(await file(join(package_dir, "node_modules", "bar", "package.json")).json()).toEqual({ + name: "bar", + version: "0.0.2", + }); + expect(await file(join(package_dir, "package.json")).text()).toEqual( + JSON.stringify( + { + name: "foo", + version: "0.0.1", + dependencies: { + bar: "^0.0.2", + }, + }, + null, + 2, + ), + ); + await access(join(package_dir, "bun.lockb")); + + { + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "add", "bar", "--only-missing"], + cwd: package_dir, + stdout: "pipe", + stdin: "pipe", + stderr: "pipe", + }); + const out = await new Response(stdout).text(); + expect(out).not.toContain("Saved lockfile"); + expect(out).not.toContain("Installed"); + expect(out.split("\n").filter(Boolean)).toStrictEqual([ + expect.stringContaining("bun add v" + Bun.version.replaceAll("-debug", "")), + ]); + } +}); + for (const pathType of ["absolute", "relative"]) { it.each(["file:///", "file://", "file:/", "file:", "", "//////"])( `should accept ${pathType} file protocol with prefix "%s"`, @@ -456,7 +601,7 @@ it("should add to devDependencies with --dev", async () => { ); await access(join(package_dir, "bun.lockb")); }); -it("should add to optionalDependencies with --optional", async () => { +it.only("should add to optionalDependencies with --optional", async () => { const urls: string[] = []; setHandler(dummyRegistry(urls)); await writeFile( @@ -466,6 +611,7 @@ it("should add to optionalDependencies with --optional", async () => { version: "0.0.1", }), ); + console.log(package_dir); const { stdout, stderr, exited } = spawn({ cmd: [bunExe(), "add", "--optional", "BaR"], cwd: package_dir, diff --git a/test/js/bun/http/bun-serve-html-entry.test.ts b/test/js/bun/http/bun-serve-html-entry.test.ts index b31c9e88fbd988..91da22627d6101 100644 --- a/test/js/bun/http/bun-serve-html-entry.test.ts +++ b/test/js/bun/http/bun-serve-html-entry.test.ts @@ -1,7 +1,6 @@ -import type { Subprocess, Server } from "bun"; -import { describe, test, expect } from "bun:test"; +import type { Subprocess } from "bun"; +import { expect, test } from "bun:test"; import { bunEnv, bunExe, tempDirWithFiles } from "harness"; -import { join } from "path"; async function getServerUrl(process: Subprocess) { // Read the port number from stdout diff --git a/test/js/bun/http/bun-serve-html.test.ts b/test/js/bun/http/bun-serve-html.test.ts index f7206fc2801382..468506bef60752 100644 --- a/test/js/bun/http/bun-serve-html.test.ts +++ b/test/js/bun/http/bun-serve-html.test.ts @@ -600,7 +600,7 @@ async function waitForServer( hostname: string; }>(); const process = Bun.spawn({ - cmd: [bunExe(), "--no-hmr", join(import.meta.dir, "bun-serve-static-fixture.js")], + cmd: [bunExe(), join(import.meta.dir, "bun-serve-static-fixture.js")], env: { ...bunEnv, NODE_ENV: undefined, diff --git a/test/js/bun/http/bun-serve-static-fixture.js b/test/js/bun/http/bun-serve-static-fixture.js index 67e499f5279388..aafa908e0fccbe 100644 --- a/test/js/bun/http/bun-serve-static-fixture.js +++ b/test/js/bun/http/bun-serve-static-fixture.js @@ -2,7 +2,9 @@ import { serve } from "bun"; let server = Bun.serve({ port: 0, - development: true, + development: { + hmr: false, + }, async fetch(req) { return new Response("Hello World", { status: 404, @@ -20,7 +22,9 @@ process.on("message", async message => { server.reload({ // omit "fetch" to check we can do server.reload without passing fetch static: routes, - development: true, + development: { + hmr: false, + }, }); }); diff --git a/test/js/bun/util/bun-file.test.ts b/test/js/bun/util/bun-file.test.ts index 0d2d111c07840e..5071caac8e49d3 100644 --- a/test/js/bun/util/bun-file.test.ts +++ b/test/js/bun/util/bun-file.test.ts @@ -1,11 +1,13 @@ import { test, expect } from "bun:test"; -import { tmpdirSync } from "harness"; +import { tempDirWithFiles } from "harness"; import { join } from "path"; import fsPromises from "fs/promises"; test("delete() and stat() should work with unicode paths", async () => { - const testDir = tmpdirSync(); - const filename = join(testDir, "🌟.txt"); + const dir = tempDirWithFiles("delete-stat-unicode-path", { + "another-file.txt": "HEY", + }); + const filename = join(dir, "🌟.txt"); expect(async () => { await Bun.file(filename).delete(); @@ -24,15 +26,18 @@ test("delete() and stat() should work with unicode paths", async () => { }); test("writer.end() should not close the fd if it does not own the fd", async () => { - const testDir = tmpdirSync(); + const dir = tempDirWithFiles("writer-end-fd", { + "tmp.txt": "HI", + }); + const filename = join(dir, "tmp.txt"); + for (let i = 0; i < 30; i++) { - const fileHandle = await fsPromises.open(testDir + "/tmp.txt", "w", 0o666); + const fileHandle = await fsPromises.open(filename, "w", 0o666); const fd = fileHandle.fd; await Bun.file(fd).writer().end(); // @ts-ignore await fsPromises.close(fd); - expect(await Bun.file(testDir + "/tmp.txt").text()).toBe(""); - await Bun.sleep(50); + expect(await Bun.file(filename).text()).toBe(""); } }); diff --git a/test/js/third_party/http2-wrapper/http2-wrapper.test.ts b/test/js/third_party/http2-wrapper/http2-wrapper.test.ts index 7faad0efb290f9..3a185d490c1d58 100644 --- a/test/js/third_party/http2-wrapper/http2-wrapper.test.ts +++ b/test/js/third_party/http2-wrapper/http2-wrapper.test.ts @@ -31,6 +31,7 @@ async function doRequest(options: AutoRequestOptions) { test("should allow http/1.1 when using http2-wrapper", async () => { { using server = Bun.serve({ + port: 0, async fetch(req) { return new Response( JSON.stringify({ @@ -60,6 +61,7 @@ test("should allow http/1.1 when using http2-wrapper", async () => { { using server = Bun.serve({ tls, + port: 0, hostname: "localhost", async fetch(req) { return new Response( From fb0f28aab9cf6b1aa69427f15874b7957f8f95ab Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 8 Feb 2025 22:52:54 -0800 Subject: [PATCH 06/25] Remove a .vscode/launch.json config that is rarely used --- .vscode/launch.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c5c33d5d986fa8..b4149c3020b375 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,19 +7,6 @@ // - "cppvsdbg" is used instead of "lldb" on Windows, because "lldb" is too slow "version": "0.2.0", "configurations": [ - { - "type": "bun", - "request": "launch", - "name": "[js] bun test [file]", - "runtime": "${workspaceFolder}/build/debug/bun-debug", - "args": ["test", "${file}"], - "cwd": "${workspaceFolder}", - "env": { - "BUN_DEBUG_QUIET_LOGS": "1", - "BUN_DEBUG_jest": "1", - "BUN_GARBAGE_COLLECTOR_LEVEL": "1", - }, - }, // bun test [file] { "type": "lldb", From 4a7e56b532dea8272ca55f110e52f8bd16fbb3c4 Mon Sep 17 00:00:00 2001 From: Minsoo Choo Date: Mon, 10 Feb 2025 05:02:38 -0500 Subject: [PATCH 07/25] Fix latest version display from update-sqlite (#17209) --- .github/workflows/update-sqlite3.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-sqlite3.yml b/.github/workflows/update-sqlite3.yml index 66b5753cca01b4..a69c6821c6a44c 100644 --- a/.github/workflows/update-sqlite3.yml +++ b/.github/workflows/update-sqlite3.yml @@ -55,7 +55,7 @@ jobs: # Convert numeric version to semantic version for display LATEST_MAJOR=$((10#$LATEST_VERSION_NUM / 1000000)) - LATEST_MINOR=$((($LATEST_VERSION_NUM / 1000) % 1000)) + LATEST_MINOR=$((($LATEST_VERSION_NUM / 10000) % 100)) LATEST_PATCH=$((10#$LATEST_VERSION_NUM % 1000)) LATEST_VERSION="$LATEST_MAJOR.$LATEST_MINOR.$LATEST_PATCH" From d814f3e6d83c913cdfa19e8fc79737e36342d231 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 10 Feb 2025 02:04:21 -0800 Subject: [PATCH 08/25] meta: fix disabling of clangd auto header insertion (#17172) --- .clangd | 3 --- .vscode/settings.json | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.clangd b/.clangd index 2d2d52668c2e9e..b0ceeaa684cdd4 100644 --- a/.clangd +++ b/.clangd @@ -6,6 +6,3 @@ CompileFlags: Diagnostics: UnusedIncludes: None - -HeaderInsertion: - IncludeBlocks: Preserve # Do not auto-include headers. diff --git a/.vscode/settings.json b/.vscode/settings.json index f7f738011685e0..11beb3deb7fcc8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -63,6 +63,7 @@ "editor.tabSize": 4, "editor.defaultFormatter": "xaver.clang-format", }, + "clangd.arguments": ["--header-insertion=never"], // JavaScript "prettier.enable": true, From a23c11e3813c895be3661dc54b73404580190ead Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Mon, 10 Feb 2025 02:09:48 -0800 Subject: [PATCH 09/25] Support `BUN_PUBLIC_*` and other env options in HTML imports (#17227) Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- cmake/tools/SetupZig.cmake | 2 +- docs/cli/bun-create.md | 89 ++++++++++++++++++- root.zig | 18 ++++ src/api/schema.zig | 5 ++ src/bake/DevServer.zig | 14 ++- src/bake/bake.zig | 11 ++- src/bake/production.zig | 6 +- src/bun.js/api/Timer.zig | 1 - src/bun.js/api/server.zig | 14 +++ src/bun.js/api/server/HTMLBundle.zig | 25 +++++- src/bun.zig | 16 ++-- src/bunfig.zig | 76 ++++++++++++---- src/cli.zig | 31 ++++--- test/js/bun/http/bun-serve-html-entry.test.ts | 13 +++ 14 files changed, 271 insertions(+), 50 deletions(-) diff --git a/cmake/tools/SetupZig.cmake b/cmake/tools/SetupZig.cmake index 9825b7831aae7d..5e97567b24bcc1 100644 --- a/cmake/tools/SetupZig.cmake +++ b/cmake/tools/SetupZig.cmake @@ -21,7 +21,7 @@ else() endif() optionx(ZIG_VERSION STRING "The zig version of the compiler to download" DEFAULT "0.14.0-dev.2987+183bb8b08") -optionx(ZIG_COMMIT STRING "The zig commit to use in oven-sh/zig" DEFAULT "63f8ed52c011beafde83216efba766492491ef4b") +optionx(ZIG_COMMIT STRING "The zig commit to use in oven-sh/zig" DEFAULT "02c57c7ee3b8fde7528c74dd06490834d2d6fae9") optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET}) if(CMAKE_BUILD_TYPE STREQUAL "Release") diff --git a/docs/cli/bun-create.md b/docs/cli/bun-create.md index 48b21450fe5ca5..1a38f8f470b7de 100644 --- a/docs/cli/bun-create.md +++ b/docs/cli/bun-create.md @@ -2,10 +2,97 @@ **Note** — You don’t need `bun create` to use Bun. You don’t need any configuration at all. This command exists to make getting started a bit quicker and easier. {% /callout %} -Template a new Bun project with `bun create`. This is a flexible command that can be used to create a new project with a `create-