Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions include/cmcpp/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ namespace cmcpp
auto encoded = cx.convert(&cx.opts.memory[ptr], worst_case_size, src, src_byte_len, src_encoding, Encoding::Utf8);
if (worst_case_size > encoded.second)
{
ptr = cx.opts.realloc(ptr, worst_case_size, 1, encoded.second);
ptr = cx.opts.realloc(ptr, worst_case_size, 1, static_cast<uint32_t>(encoded.second));
assert(ptr + encoded.second <= cx.opts.memory.size());
}
return std::make_pair(ptr, encoded.second);
return std::make_pair(ptr, static_cast<uint32_t>(encoded.second));
}

inline std::pair<uint32_t, uint32_t> store_utf16_to_utf8(LiftLowerContext &cx, const void *src, uint32_t src_code_units)
Expand All @@ -62,7 +62,7 @@ namespace cmcpp
auto encoded = cx.convert(&cx.opts.memory[ptr], worst_case_size, src, src_code_units, Encoding::Utf8, Encoding::Utf16);
if (encoded.second < worst_case_size)
{
ptr = cx.opts.realloc(ptr, worst_case_size, 2, encoded.second);
ptr = cx.opts.realloc(ptr, worst_case_size, 2, static_cast<uint32_t>(encoded.second));
assert(ptr == align_to(ptr, 2));
assert(ptr + encoded.second <= cx.opts.memory.size());
}
Expand Down Expand Up @@ -103,7 +103,7 @@ namespace cmcpp
const size_t src_byte_length = src_code_units * ValTrait<T>::char_size;

assert(src_code_units <= MAX_STRING_BYTE_LENGTH);
uint32_t ptr = cx.opts.realloc(0, 0, 2, src_byte_length);
uint32_t ptr = cx.opts.realloc(0, 0, 2, static_cast<uint32_t>(src_byte_length));
trap_if(cx, ptr != align_to(ptr, 2));
trap_if(cx, ptr + src_code_units > cx.opts.memory.size());
uint32_t dst_byte_length = 0;
Expand All @@ -118,9 +118,9 @@ namespace cmcpp
else
{
// If it doesn't, convert it to a UTF-16 sequence
uint32_t worst_case_size = 2 * src_code_units;
uint32_t worst_case_size = static_cast<uint32_t>(2 * src_code_units);
trap_if(cx, worst_case_size > MAX_STRING_BYTE_LENGTH, "Worst case size exceeds maximum string byte length");
ptr = cx.opts.realloc(ptr, src_byte_length, 2, worst_case_size);
ptr = cx.opts.realloc(ptr, static_cast<uint32_t>(src_byte_length), 2, worst_case_size);
trap_if(cx, ptr != align_to(ptr, 2), "Pointer misaligned");
trap_if(cx, ptr + worst_case_size > cx.opts.memory.size(), "Out of bounds access");

Expand All @@ -147,7 +147,7 @@ namespace cmcpp
uint32_t destPtr = ptr + (2 * dst_byte_length);
uint32_t destLen = worst_case_size - (2 * dst_byte_length);
void *srcPtr = (char *)src + dst_byte_length * ValTrait<T>::char_size;
uint32_t srcLen = (src_code_units - dst_byte_length) * ValTrait<T>::char_size;
uint32_t srcLen = static_cast<uint32_t>((src_code_units - dst_byte_length) * ValTrait<T>::char_size);
auto encoded = cx.convert(&cx.opts.memory[destPtr], destLen, srcPtr, srcLen, src_encoding, Encoding::Utf16);

// Add special tag to indicate the string is a UTF-16 string ---
Expand All @@ -158,7 +158,7 @@ namespace cmcpp
}
if (dst_byte_length < src_code_units)
{
ptr = cx.opts.realloc(ptr, src_code_units, 2, dst_byte_length);
ptr = cx.opts.realloc(ptr, static_cast<uint32_t>(src_code_units), 2, dst_byte_length);
trap_if(cx, ptr != align_to(ptr, 2), "Pointer misaligned");
trap_if(cx, ptr + dst_byte_length > cx.opts.memory.size(), "Out of bounds access");
}
Expand All @@ -179,18 +179,18 @@ namespace cmcpp
if (src_tagged_code_units & UTF16_TAG)
{
src_simple_encoding = Encoding::Utf16;
src_code_units = src_tagged_code_units ^ UTF16_TAG;
src_code_units = static_cast<uint32_t>(src_tagged_code_units ^ UTF16_TAG);
}
else
{
src_simple_encoding = Encoding::Latin1;
src_code_units = src_tagged_code_units;
src_code_units = static_cast<uint32_t>(src_tagged_code_units);
}
}
else
{
src_simple_encoding = src_encoding;
src_code_units = src_tagged_code_units;
src_code_units = static_cast<uint32_t>(src_tagged_code_units);
}

switch (cx.opts.string_encoding)
Expand Down Expand Up @@ -300,7 +300,7 @@ namespace cmcpp
retVal.encoding = encoding;
}
retVal.resize(host_byte_length);
auto decoded = cx.convert(retVal.data(), host_byte_length, (void *)&cx.opts.memory[ptr], byte_length, encoding, ValTrait<T>::encoding == Encoding::Latin1_Utf16 ? encoding : ValTrait<T>::encoding);
auto decoded = cx.convert(retVal.data(), host_byte_length, (void *)&cx.opts.memory[ptr], static_cast<uint32_t>(byte_length), encoding, ValTrait<T>::encoding == Encoding::Latin1_Utf16 ? encoding : ValTrait<T>::encoding);
if ((decoded.second / char_size) < host_byte_length)
{
retVal.resize(decoded.second / char_size);
Expand Down
9 changes: 8 additions & 1 deletion samples/wamr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,14 @@ target_include_directories(wamr
)

if (MSVC)
target_compile_options(wamr PRIVATE /EHsc /permissive-)
target_compile_options(wamr PRIVATE
/EHsc
/permissive-
/wd4244 # conversion from 'type1' to 'type2', possible loss of data
/wd4267 # conversion from 'size_t' to 'type', possible loss of data
/wd4305 # truncation from 'type1' to 'type2'
/wd4309 # truncation of constant value
)
else()
target_compile_options(wamr PRIVATE -Wno-error=maybe-uninitialized -Wno-error=jump-misses-init)
endif()
Expand Down
2 changes: 1 addition & 1 deletion samples/wamr/generated/sample_wamr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

// Generated WAMR bindings for package: example:sample
// These symbol arrays can be used with wasm_runtime_register_natives_raw()
// NOTE: You must implement the functions declared in the imports namespace
// NOTE: You must implement the functions declared in the host namespace
// (See sample.hpp for declarations, provide implementations in your host code)

using namespace cmcpp;
Expand Down
10 changes: 10 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_link_options(-fprofile-instr-generate)
endif()

# Suppress narrowing conversion warnings on MSVC for WebAssembly 32-bit ABI
if(MSVC)
add_compile_options(
/wd4244 # conversion from 'type1' to 'type2', possible loss of data
/wd4267 # conversion from 'size_t' to 'type', possible loss of data
/wd4305 # truncation from 'type1' to 'type2'
/wd4309 # truncation of constant value
)
endif()

find_package(doctest CONFIG REQUIRED)
find_package(ICU REQUIRED COMPONENTS uc dt in io)

Expand Down
24 changes: 20 additions & 4 deletions test/generate_test_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
from typing import List, Tuple
import argparse

# Force UTF-8 encoding for stdout on Windows
if sys.platform == 'win32':
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')

# ANSI color codes
class Colors:
RED = '\033[0;31m'
Expand All @@ -31,6 +37,16 @@ def color(cls, text: str, color_code: str) -> str:
return f"{color_code}{text}{cls.NC}"
return text

# Platform-specific symbols
if sys.platform == 'win32':
CHECKMARK = "OK"
CROSSMARK = "FAIL"
SKIPMARK = "SKIP"
else:
CHECKMARK = "✓"
CROSSMARK = "✗"
SKIPMARK = "⊘"

def find_wit_files(directory: Path) -> List[Path]:
"""Recursively find all .wit files in a directory"""
return sorted(directory.rglob("*.wit"))
Expand Down Expand Up @@ -174,15 +190,15 @@ def main():
success, message = generate_stub(wit_file, output_prefix, codegen_tool, args.verbose)

if success:
print(Colors.color("✓", Colors.GREEN))
print(Colors.color(CHECKMARK, Colors.GREEN))
if args.verbose and message:
print(f" {message}")
success_count += 1
elif "No output files generated" in message:
print(Colors.color("⊘ (no output)", Colors.YELLOW))
print(Colors.color(f"{SKIPMARK} (no output)", Colors.YELLOW))
skipped_count += 1
else:
print(Colors.color("✗", Colors.RED))
print(Colors.color(CROSSMARK, Colors.RED))
if args.verbose:
print(f" Error: {message}")
failure_count += 1
Expand All @@ -203,7 +219,7 @@ def main():
if args.verbose:
print(f" Error: {error}")

print(f"\n{Colors.color('✓', Colors.GREEN)} Stub generation complete!")
print(f"\n{Colors.color(CHECKMARK, Colors.GREEN)} Stub generation complete!")
print(f"Output directory: {output_dir}")

# Exit with 0 if we have successful generations, even with some failures
Expand Down
2 changes: 1 addition & 1 deletion test/host-util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Heap
}

uint32_t ret = align_to(last_alloc, alignment);
last_alloc = ret + new_size;
last_alloc = static_cast<uint32_t>(ret + new_size);
if (last_alloc > memory.size())
{
trap("oom");
Expand Down
39 changes: 38 additions & 1 deletion test/validate_all_wit_bindgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@ def generate_stub(wit_path, stub_name):
except Exception as e:
return False, str(e)

def check_if_empty_stub(stub_name):
"""Check if generated stub only contains empty namespaces"""
stub_file = GENERATED_DIR / f"{stub_name}.hpp"

try:
with open(stub_file, 'r') as f:
content = f.read()
# Check for the marker comment that indicates empty interfaces
if "This WIT file contains no concrete interface definitions" in content:
# Also verify it only has empty namespaces
if "namespace host {}" in content and "namespace guest {}" in content:
return True
except Exception:
pass

return False

def compile_stub(stub_name):
"""Compile a generated stub"""
stub_file = GENERATED_DIR / f"{stub_name}.hpp"
Expand Down Expand Up @@ -107,6 +124,10 @@ def process_wit_file(wit_path):
gen_ok, gen_error = generate_stub(wit_path, stub_name)

if gen_ok:
# Check if this generated an empty stub (references external packages only)
if check_if_empty_stub(stub_name):
return rel_path, 'empty_stub', 'Generated empty stub (references external packages only)'

# Compile stub
compile_ok, compile_error = compile_stub(stub_name)

Expand Down Expand Up @@ -142,11 +163,13 @@ def main():
'gen_success': 0,
'gen_failed': 0,
'compile_success': 0,
'compile_failed': 0
'compile_failed': 0,
'empty_stubs': 0
}

failed_generation = []
failed_compilation = []
empty_stub_files = []

# Process files in parallel
with ThreadPoolExecutor(max_workers=num_workers) as executor:
Expand All @@ -163,6 +186,11 @@ def main():
stats['gen_success'] += 1
stats['compile_success'] += 1
status = f"{GREEN}✓{RESET}"
elif result == 'empty_stub':
stats['gen_success'] += 1
stats['empty_stubs'] += 1
status = f"{YELLOW}⊘{RESET}"
empty_stub_files.append((rel_path, error))
elif result == 'compile_failed':
stats['gen_success'] += 1
stats['compile_failed'] += 1
Expand Down Expand Up @@ -191,9 +219,18 @@ def main():
print(f" Total WIT files: {stats['total']}")
print(f" Generation successful: {stats['gen_success']} ({stats['gen_success']/stats['total']*100:.1f}%)")
print(f" Generation failed: {stats['gen_failed']}")
print(f" Empty stubs (no types): {stats['empty_stubs']}")
print(f" Compilation successful: {stats['compile_success']} ({stats['compile_success']/stats['total']*100:.1f}%)")
print(f" Compilation failed: {stats['compile_failed']}")

if empty_stub_files:
print(f"\n{YELLOW}Empty stubs ({len(empty_stub_files)} files):{RESET}")
print(f"These files only reference external packages and contain no concrete definitions:")
for rel_path, reason in empty_stub_files[:10]:
print(f" - {rel_path}")
if len(empty_stub_files) > 10:
print(f" ... and {len(empty_stub_files) - 10} more")

if failed_generation:
print(f"\n{YELLOW}Failed generation ({len(failed_generation)} files):{RESET}")
for rel_path, error in failed_generation[:10]: # Show first 10
Expand Down
2 changes: 2 additions & 0 deletions tools/wit-codegen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ add_executable(wit-codegen
wit_parser.cpp
code_generator.cpp
type_mapper.cpp
package_registry.cpp
dependency_resolver.cpp
${ANTLR_GENERATED_SOURCES}
)

Expand Down
Loading