@@ -528,8 +528,8 @@ <h1>Null-Safe Clang Playground</h1>
528528
529529 < select id ="examplesSelect " class ="examples-select ">
530530 < option value ="null-check " selected > Null Check Example</ option >
531- < option value ="array " > Array Safety </ option >
532- < option value ="function " > Function Parameters </ option >
531+ < option value ="function-invalidates " > Function Invalidates Check </ option >
532+ < option value ="nonnull-annotation " > _Nonnull Annotations </ option >
533533 </ select >
534534
535535 < button id ="shareBtn " class ="btn btn-secondary ">
@@ -564,9 +564,6 @@ <h1>Null-Safe Clang Playground</h1>
564564 < div class ="pane ">
565565 < div class ="pane-header ">
566566 < span > Compiler Output</ span >
567- < div class ="pane-actions ">
568- < span id ="outputStats " class ="icon-btn " style ="cursor: default; "> </ span >
569- </ div >
570567 </ div >
571568 < div class ="output-container ">
572569 < div class ="output-section ">
@@ -594,7 +591,6 @@ <h1>Null-Safe Clang Playground</h1>
594591 const shareBtn = document . getElementById ( 'shareBtn' ) ;
595592 const loadingBar = document . getElementById ( 'loadingBar' ) ;
596593 const toast = document . getElementById ( 'toast' ) ;
597- const outputStats = document . getElementById ( 'outputStats' ) ;
598594 const divider = document . getElementById ( 'divider' ) ;
599595 const outputDivider = document . getElementById ( 'outputDivider' ) ;
600596
@@ -608,40 +604,29 @@ <h1>Null-Safe Clang Playground</h1>
608604void unsafe(int* data) {
609605 *data = 42; // warning - data might be null!
610606}` ,
611- 'array ' : `#include <stdlib.h>
607+ 'function-invalidates ' : `void modify(int** ptr); // external function
612608
613- int main(void) {
614- int *arr = malloc(10 * sizeof(int));
609+ void example(int* data) {
610+ if (data) {
611+ *data = 1; // OK - checked
615612
616- arr[0] = 42 ; // warning - malloc can return NULL
613+ modify(&data) ; // might modify data!
617614
618- if (arr) {
619- arr[0] = 42; // OK - arr is non-null here
620- free(arr);
615+ *data = 2; // warning - data could be null now!
621616 }
622-
623- return 0;
624617}` ,
625- 'function' : `void process(int *data) {
626- *data = 42; // warning - data might be null
627- }
628-
629- void process_safe(int *data) {
630- if (data) {
631- *data = 42; // OK - checked for null
632- }
633- }
634-
635- void guaranteed(int * _Nonnull data) {
618+ 'nonnull-annotation' : `// Use _Nonnull to require non-null arguments
619+ void process_nonnull(int * _Nonnull data) {
636620 *data = 42; // OK - caller must pass non-null
637621}
638622
639- int main(void) {
640- int x = 0;
641- process(&x);
642- process_safe(&x);
643- guaranteed(&x);
644- return 0;
623+ void caller(void) {
624+ int* nullable = 0;
625+ int x = 10;
626+
627+ process_nonnull(0); // error!
628+ process_nonnull(nullable); // error!
629+ process_nonnull(&x); // OK
645630}`
646631 } ;
647632
@@ -652,6 +637,7 @@ <h1>Null-Safe Clang Playground</h1>
652637 let isCompiling = false ;
653638 let isDragging = false ;
654639 let scriptUrl = null ; // URL for worker to load clang.js
640+ let wasmBinary = null ; // Pre-loaded WASM binary to share with workers
655641
656642 // Toast notification
657643 function showToast ( message , duration = 2000 ) {
@@ -743,79 +729,40 @@ <h1>Null-Safe Clang Playground</h1>
743729 status . className = 'status' ;
744730 loadingBar . classList . add ( 'active' ) ;
745731
746- let capturedStdout = '' ;
747- let capturedStderr = '' ;
748-
749732 try {
750733 // Determine script URL
751734 const isDev = window . location . hostname === 'localhost' || window . location . hostname === '127.0.0.1' ;
752735 scriptUrl = isDev
753736 ? './clang.js'
754737 : 'https://github.com/cs01/llvm-project/releases/latest/download/clang-nullsafe.js' ;
755738
756- // Create a temporary worker to get the version
757- const worker = new Worker ( 'compiler-worker.js' ) ;
758-
759- let versionResolved = false ;
760- let loadTimeout = setTimeout ( ( ) => {
761- if ( ! versionResolved ) {
762- worker . terminate ( ) ;
763- throw new Error ( 'Compiler initialization timeout' ) ;
764- }
765- } , 60000 ) ; // 60 second timeout
766-
767- worker . onmessage = function ( e ) {
768- const { type, text, error } = e . data ;
769-
770- if ( type === 'ready' ) {
771- capturedStdout = '' ;
772- capturedStderr = '' ;
773- worker . postMessage ( {
774- type : 'compile' ,
775- code : 'int main() { return 0; }' ,
776- extraFlags : [ '--version' ]
777- } ) ;
778- } else if ( type === 'stdout' ) {
779- capturedStdout += text + '\n' ;
780- } else if ( type === 'stderr' ) {
781- capturedStderr += text + '\n' ;
782- } else if ( type === 'complete' ) {
783- if ( ! versionResolved ) {
784- versionResolved = true ;
785- clearTimeout ( loadTimeout ) ;
786-
787- const versionOutput = capturedStdout + capturedStderr ;
788- const versionMatch = versionOutput . match ( / c l a n g v e r s i o n ( [ ^ \s ] + ) / ) ;
789- if ( versionMatch ) {
790- clangVersion = versionMatch [ 1 ] ;
791- document . querySelector ( '.output-section-header' ) . textContent =
792- `Null-Safe Clang ${ clangVersion } ` ;
793- }
794-
795- worker . terminate ( ) ;
796-
797- status . textContent = 'Ready to compile' ;
798- status . className = 'status ready' ;
799- compileBtn . disabled = false ;
800- loadingBar . classList . remove ( 'active' ) ;
801- showToast ( 'Compiler loaded successfully!' ) ;
802-
803- setTimeout ( ( ) => compile ( ) , 500 ) ;
804- }
805- } else if ( type === 'error' ) {
806- clearTimeout ( loadTimeout ) ;
807- worker . terminate ( ) ;
808- throw new Error ( error ) ;
809- }
810- } ;
739+ const wasmUrl = isDev
740+ ? './clang.wasm'
741+ : 'https://github.com/cs01/llvm-project/releases/latest/download/clang-nullsafe.wasm' ;
742+
743+ // Pre-load the WASM binary ONCE
744+ console . log ( 'Downloading WASM binary...' ) ;
745+ const wasmResponse = await fetch ( wasmUrl ) ;
746+ wasmBinary = await wasmResponse . arrayBuffer ( ) ;
747+ console . log ( 'WASM binary loaded:' , wasmBinary . byteLength , 'bytes' ) ;
748+
749+ // Now test with a worker to get the version
750+ const versionResult = await compileCode ( 'int main() { return 0; }' , [ '--version' ] ) ;
751+ const versionOutput = versionResult . stdout + versionResult . stderr ;
752+ const versionMatch = versionOutput . match ( / c l a n g v e r s i o n ( [ ^ \s ] + ) / ) ;
753+ if ( versionMatch ) {
754+ clangVersion = versionMatch [ 1 ] ;
755+ document . querySelector ( '.output-section-header' ) . textContent =
756+ `Null-Safe Clang ${ clangVersion } ` ;
757+ }
811758
812- worker . onerror = function ( error ) {
813- clearTimeout ( loadTimeout ) ;
814- worker . terminate ( ) ;
815- throw error ;
816- } ;
759+ status . textContent = 'Ready to compile' ;
760+ status . className = 'status ready' ;
761+ compileBtn . disabled = false ;
762+ loadingBar . classList . remove ( 'active' ) ;
763+ showToast ( 'Compiler loaded successfully!' ) ;
817764
818- worker . postMessage ( { type : 'load' , scriptUrl } ) ;
765+ setTimeout ( ( ) => compile ( ) , 500 ) ;
819766
820767 } catch ( error ) {
821768 status . textContent = 'Failed to load compiler' ;
@@ -840,33 +787,30 @@ <h1>Null-Safe Clang Playground</h1>
840787 status . className = 'status compiling' ;
841788 outputNullsafe . innerHTML = '' ;
842789 outputMainline . innerHTML = '' ;
843- outputStats . textContent = '' ;
844790 loadingBar . classList . add ( 'active' ) ;
845791
846792 const startTime = performance . now ( ) ;
847793
848794 try {
849795 const code = editor . value ;
850796
851- // Compile with null-safety enabled (default)
852- const nullsafeFlags = [ ] ;
853- const nullsafeResult = await compileCode ( code , nullsafeFlags ) ;
854-
855- // Compile with nullability warnings suppressed (simulates mainline)
856- const mainlineFlags = [ '-Wno-nullability' ] ;
857- const mainlineResult = await compileCode ( code , mainlineFlags ) ;
797+ // Compile both versions in parallel
798+ const [ nullsafeResult , mainlineResult ] = await Promise . all ( [
799+ compileCode ( code , [ ] ) ,
800+ compileCode ( code , [ '-Wno-nullability' ] )
801+ ] ) ;
858802
859803 const duration = ( performance . now ( ) - startTime ) . toFixed ( 0 ) ;
860804
861805 // Build command strings
862806 const baseArgs = '-fsyntax-only --target=wasm32-unknown-emscripten' ;
863- const nullsafeCmd = `$ clang ${ baseArgs } ${ nullsafeFlags . length ? ' ' + nullsafeFlags . join ( ' ' ) : '' } input.c` ;
864- const mainlineCmd = `$ clang ${ baseArgs } ${ mainlineFlags . length ? ' ' + mainlineFlags . join ( ' ' ) : '' } input.c` ;
807+ const nullsafeCmd = `$ clang ${ baseArgs } input.c` ;
808+ const mainlineCmd = `$ clang ${ baseArgs } -Wno-nullability input.c` ;
865809
866810 // Update headers with clearer titles
867811 const headers = document . querySelectorAll ( '.output-section-header' ) ;
868- headers [ 0 ] . textContent = `With Null Warnings (v${ clangVersion } )` ;
869- headers [ 1 ] . textContent = `Without Null Warnings (v${ clangVersion } )` ;
812+ headers [ 0 ] . textContent = `With Null Warnings (clang v${ clangVersion } )` ;
813+ headers [ 1 ] . textContent = `Without Null Warnings (clang v${ clangVersion } )` ;
870814
871815 // Count nullability warnings to detect missed bugs
872816 const nullWarningCount = ( nullsafeResult . stderr . match ( / \[ - W n u l l a b i l i t y \] / g) || [ ] ) . length ;
@@ -890,18 +834,6 @@ <h1>Null-Safe Clang Playground</h1>
890834 }
891835 }
892836
893- // Calculate stats from null-safe results
894- const errorCount = ( nullsafeResult . stderr . match ( / e r r o r : / g) || [ ] ) . length ;
895- const warningCount = ( nullsafeResult . stderr . match ( / w a r n i n g : / g) || [ ] ) . length ;
896-
897- let stats = [ ] ;
898- if ( errorCount > 0 ) stats . push ( `${ errorCount } error${ errorCount !== 1 ? 's' : '' } ` ) ;
899- if ( warningCount > 0 ) stats . push ( `${ warningCount } warning${ warningCount !== 1 ? 's' : '' } ` ) ;
900- if ( nullWarningCount > 0 ) stats . push ( `(${ nullWarningCount } null bug${ nullWarningCount !== 1 ? 's' : '' } )` ) ;
901- stats . push ( `${ duration } ms` ) ;
902-
903- outputStats . textContent = stats . join ( ' • ' ) ;
904-
905837 status . textContent = 'Ready to compile' ;
906838 status . className = 'status ready' ;
907839 } catch ( error ) {
@@ -910,7 +842,6 @@ <h1>Null-Safe Clang Playground</h1>
910842 outputMainline . innerHTML = `<span class="error">Compilation failed: ${ errorMsg } \n\nCheck console for details.</span>` ;
911843 status . textContent = 'Compilation error' ;
912844 status . className = 'status error' ;
913- outputStats . textContent = '✗ Failed' ;
914845 console . error ( 'Compilation error:' , error ) ;
915846 } finally {
916847 isCompiling = false ;
@@ -938,6 +869,7 @@ <h1>Null-Safe Clang Playground</h1>
938869 const { type, text, error, exitCode } = e . data ;
939870
940871 if ( type === 'ready' ) {
872+ // Worker is ready, send compile request
941873 worker . postMessage ( {
942874 type : 'compile' ,
943875 code,
@@ -967,7 +899,12 @@ <h1>Null-Safe Clang Playground</h1>
967899 reject ( error ) ;
968900 } ;
969901
970- worker . postMessage ( { type : 'load' , scriptUrl } ) ;
902+ // Send the pre-loaded WASM binary to the worker
903+ worker . postMessage ( {
904+ type : 'load' ,
905+ scriptUrl,
906+ wasmBinary // Share the pre-loaded WASM
907+ } ) ;
971908 } ) ;
972909 }
973910
0 commit comments