@five-vm/cli
Version:
High-performance CLI for Five VM development with WebAssembly integration
624 lines • 26.8 kB
JavaScript
/**
* Five Compiler WASM Integration
*
* Real integration with Five VM WASM bindings for DSL compilation,
* ABI generation, and bytecode optimization.
*/
import { readFile, writeFile } from "fs/promises";
import { existsSync, readFileSync } from "fs";
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";
import { ConfigManager } from "../config/ConfigManager.js";
// Real Five VM WASM imports
let WasmFiveCompiler;
let FiveVMWasm;
let BytecodeAnalyzer;
let WasmCompilationOptions;
let wasmModuleRef = null;
export class FiveCompilerWasm {
compiler = null;
logger;
initialized = false;
constructor(logger) {
this.logger = logger;
}
/**
* Initialize the compiler with real Five VM WASM module
*/
async initialize() {
try {
// Try multiple candidate locations (mirrors vm.ts logic)
const cfg = await ConfigManager.getInstance().get();
const prefer = cfg.wasm?.loader || "auto";
const configured = Array.isArray(cfg.wasm?.modulePaths)
? cfg.wasm.modulePaths
: [];
const nodeCandidates = ["../../five_vm_wasm.js", "../five_vm_wasm.js"];
const bundlerCandidates = [
"../../assets/vm/five_vm_wasm.js",
"../assets/vm/five_vm_wasm.js",
];
let candidates = [];
candidates.push(...configured);
if (prefer === "node") {
candidates.push(...nodeCandidates);
}
else if (prefer === "bundler") {
candidates.push(...bundlerCandidates);
}
else {
candidates.push(...nodeCandidates, ...bundlerCandidates);
}
let wasmModule = null;
const tried = [];
for (const candidate of candidates) {
try {
// eslint-disable-next-line no-await-in-loop
const mod = await import(candidate);
// If initSync is available, prefer initializing with local file bytes to avoid fetch/file URL issues
if (mod && typeof mod.initSync === "function") {
try {
const here = dirname(fileURLToPath(import.meta.url));
const wasmFiles = [
resolve(here, "../five_vm_wasm_bg.wasm"), // dist/five_vm_wasm_bg.wasm
resolve(here, "../../five_vm_wasm_bg.wasm"), // five-cli/five_vm_wasm_bg.wasm (unlikely)
resolve(here, "../assets/vm/five_vm_wasm_bg.wasm"), // dist/assets/vm
resolve(here, "../../assets/vm/five_vm_wasm_bg.wasm"), // assets/vm
];
for (const wf of wasmFiles) {
if (existsSync(wf)) {
// eslint-disable-next-line no-await-in-loop
mod.initSync(readFileSync(wf));
break;
}
}
}
catch (syncErr) {
tried.push({ path: candidate, error: syncErr });
}
}
// Initialize node-friendly wasm-pack bundle if it exposes a default init (fallback)
if (mod && typeof mod.default === "function") {
try {
// eslint-disable-next-line no-await-in-loop
await mod.default();
}
catch (initErr) {
tried.push({ path: candidate, error: initErr });
}
}
if (mod &&
(mod.WasmFiveCompiler || mod.FiveCompilerWasm) &&
mod.FiveVMWasm) {
wasmModule = mod;
break;
}
tried.push({ path: candidate, error: "Missing expected exports" });
}
catch (e) {
tried.push({ path: candidate, error: e });
}
}
if (!wasmModule) {
const attempted = tried
.map((t) => ` - ${t.path}: ${t.error instanceof Error ? t.error.message : String(t.error)}`)
.join("\n");
throw new Error(`Failed to load WASM compiler module. Attempted:\n${attempted}`);
}
wasmModuleRef = wasmModule;
WasmFiveCompiler =
wasmModule.WasmFiveCompiler || wasmModule.FiveCompilerWasm;
FiveVMWasm = wasmModule.FiveVMWasm;
BytecodeAnalyzer = wasmModule.BytecodeAnalyzer;
WasmCompilationOptions = wasmModule.WasmCompilationOptions;
// Initialize the compiler instance
this.compiler = new WasmFiveCompiler();
this.initialized = true;
// WASM compiler initialized silently
}
catch (error) {
throw this.createCompilerError(`Five VM WASM modules not found. Please run "npm run build:wasm" to build the required WebAssembly modules. Error: ${error}`, error);
}
}
/**
* Compile Five DSL source code from string (SDK compatibility)
*/
async compile(source, options) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
const startTime = Date.now();
try {
// Compile source silently
// Use enhanced WASM compiler methods with rich error messages
let result;
// Compile using WASM bindings
try {
// Use unified compilation method (module already loaded in initialize)
if (!WasmCompilationOptions && wasmModuleRef) {
WasmCompilationOptions = wasmModuleRef.WasmCompilationOptions;
}
const compilationOptions = new WasmCompilationOptions()
.with_mode(options?.target || "deployment")
.with_optimization_level(options?.optimizationLevel || "v2")
.with_v2_preview(true)
.with_constraint_cache(false)
.with_enhanced_errors(false)
.with_metrics(false)
.with_error_format("terminal");
// Note: not setting source_file since we have source string
// Execute compilation
result = this.compiler.compile(source, compilationOptions);
// Process compilation result silently
// Log compiler errors for debugging if present
if (result.compiler_errors && result.compiler_errors.length > 0) {
this.logger.debug(`WASM returned ${result.compiler_errors.length} compiler errors`);
for (const error of result.compiler_errors) {
this.logger.debug(`Compiler error details:`, error);
// Extract error details using WASM getters
try {
const message = error.message ? error.message : "No message";
const code = error.code ? error.code : "No code";
const severity = error.severity ? error.severity : "Unknown";
const category = error.category ? error.category : "Unknown";
this.logger.error(`Detailed error: [${code}] ${severity} - ${message}`);
this.logger.error(`Category: ${category}`);
// Try to get location information
if (error.location) {
const location = error.location;
this.logger.error(`Location: line ${location.line}, column ${location.column}`);
}
}
catch (e) {
this.logger.debug(`Failed to extract error details:`, e);
this.logger.debug(`Raw error object:`, error);
}
}
}
}
catch (wasmError) {
this.logger.debug("WASM compilation threw an error:", wasmError);
throw wasmError;
}
// Transform result format to match expected SDK interface
if (result.success && result.bytecode) {
return {
success: true,
bytecode: result.bytecode,
abi: result.abi,
metadata: result.metadata,
};
}
else {
return {
success: false,
errors: result.compiler_errors || [],
metadata: result.metadata,
};
}
}
catch (error) {
throw this.createCompilerError(`Compilation error: ${error instanceof Error ? error.message : "Unknown error"}`, error);
}
}
/**
* Compile Five DSL source code from file (CLI compatibility)
*/
async compileFile(options) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
const startTime = Date.now();
try {
// Read source file
const sourceCode = await readFile(options.sourceFile, "utf8");
this.logger.debug(`Compiling source file: ${options.sourceFile}`);
// Use enhanced WASM compiler methods with rich error messages
let result;
// Compile using WASM bindings
try {
// Use unified compilation method (module already loaded in initialize)
if (!WasmCompilationOptions && wasmModuleRef) {
WasmCompilationOptions = wasmModuleRef.WasmCompilationOptions;
}
const compilationOptions = new WasmCompilationOptions()
.with_mode(options.target || "deployment")
.with_optimization_level(options.optimizationLevel || "v2")
.with_v2_preview(true)
.with_constraint_cache(options.enable_constraint_cache !== false)
.with_enhanced_errors(true)
.with_metrics(true)
.with_error_format("terminal")
.with_source_file(options.sourceFile);
// Execute compilation
result = this.compiler.compile(sourceCode, compilationOptions);
// Process compilation result silently
// Log compiler errors for debugging if present
if (result.compiler_errors && result.compiler_errors.length > 0) {
this.logger.debug(`WASM returned ${result.compiler_errors.length} compiler errors`);
for (const error of result.compiler_errors) {
this.logger.debug(`Compiler error details:`, error);
// Extract error details using WASM getters
try {
const message = error.message ? error.message : "No message";
const code = error.code ? error.code : "No code";
const severity = error.severity ? error.severity : "Unknown";
const category = error.category ? error.category : "Unknown";
this.logger.error(`Detailed error: [${code}] ${severity} - ${message}`);
this.logger.error(`Category: ${category}`);
// Try to get location information
if (error.location) {
const location = error.location;
this.logger.error(`Location: line ${location.line}, column ${location.column}`);
}
}
catch (e) {
this.logger.debug(`Failed to extract error details:`, e);
this.logger.debug(`Raw error object:`, error);
}
}
}
}
catch (wasmError) {
this.logger.error("WASM compiler threw exception:", wasmError);
// If WASM throws exception, create a structured error response
result = {
success: false,
bytecode: null,
bytecode_size: 0,
compilation_time: 0,
compiler_errors: [
{
code: "E9999",
severity: "error",
category: "wasm_exception",
message: wasmError instanceof Error
? wasmError.message
: String(wasmError),
description: "WASM compiler threw an exception",
location: null,
suggestions: [],
},
],
error_count: 1,
warning_count: 0,
};
}
// Check if compilation failed but we have enhanced error information
if (!result.success &&
result.compiler_errors &&
result.compiler_errors.length > 0) {
// Handle compilation errors
// Log enhanced error details using WASM formatting methods
this.logger.debug(`Enhanced Error Count: ${result.error_count}`);
// Try to get formatted output from WASM
try {
const terminalOutput = result.format_all_terminal
? result.format_all_terminal()
: null;
if (terminalOutput) {
this.logger.debug("Terminal formatted errors:", terminalOutput);
}
const jsonOutput = result.format_all_json
? result.format_all_json()
: null;
if (jsonOutput) {
this.logger.debug("JSON formatted errors:", jsonOutput);
}
}
catch (formatError) {
this.logger.debug("Failed to get formatted errors:", formatError);
}
}
const compilationTime = Date.now() - startTime;
// Use WASM-provided formatting methods to get structured error information
let convertedErrors = [];
if (result.compiler_errors && result.compiler_errors.length > 0) {
try {
// Try to get JSON formatted errors from WASM
const jsonErrors = result.format_all_json
? result.format_all_json()
: null;
if (jsonErrors) {
const parsedErrors = JSON.parse(jsonErrors);
convertedErrors = parsedErrors.map((error) => ({
type: "enhanced",
...error,
// Ensure proper structure for CLI display
code: error.code || "E0000",
severity: error.severity || "error",
category: error.category || "compilation",
message: error.message || "Unknown error",
}));
}
else {
// Fallback: create basic errors from the result
convertedErrors = [
{
type: "enhanced",
code: "E0004",
severity: "error",
category: "compilation",
message: "InvalidScript",
description: "The script contains syntax or semantic errors",
location: undefined,
suggestions: [],
},
];
}
}
catch (parseError) {
this.logger.debug("Failed to parse JSON errors from WASM:", parseError);
// Fallback to basic error
convertedErrors = [
{
type: "enhanced",
code: "E0004",
severity: "error",
category: "compilation",
message: "InvalidScript",
description: "Compilation failed with enhanced error system",
location: undefined,
suggestions: [],
},
];
}
}
const compilationResult = {
success: result.success,
bytecode: result.bytecode ? new Uint8Array(result.bytecode) : undefined,
abi: result.abi || undefined,
errors: convertedErrors,
warnings: convertedErrors.filter((e) => e.severity === "warning") || [],
metrics: {
compilationTime: result.compilation_time || compilationTime,
bytecodeSize: result.bytecode_size || 0,
memoryUsed: 0, // Not available from WASM
optimizationTime: 0,
instructionCount: 0, // Would need analysis
functionCount: 0, // Would need analysis
},
};
// Write output file if specified and compilation succeeded
if (options.outputFile && compilationResult.bytecode) {
await writeFile(options.outputFile, compilationResult.bytecode);
this.logger.debug(`Bytecode written to: ${options.outputFile}`);
}
// Write ABI file if generated
if (options.abiOutputFile && compilationResult.abi) {
await writeFile(options.abiOutputFile, JSON.stringify(compilationResult.abi, null, 2));
this.logger.debug(`ABI written to: ${options.abiOutputFile}`);
}
this.logger.debug(`Compilation completed in ${compilationTime}ms`);
return compilationResult;
}
catch (error) {
const compilationTime = Date.now() - startTime;
return {
success: false,
errors: [
{
type: "runtime",
message: error instanceof Error ? error.message : String(error),
sourceLocation: options.sourceFile,
},
],
warnings: [],
metrics: {
compilationTime,
memoryUsed: 0,
optimizationTime: 0,
bytecodeSize: 0,
instructionCount: 0,
functionCount: 0,
},
};
}
}
/**
* Generate ABI from DSL source using real WASM compiler
*/
async generateABI(sourceCode) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const abi = this.compiler.generate_abi(sourceCode);
return JSON.parse(abi);
}
catch (error) {
throw this.createCompilerError("ABI generation failed", error);
}
}
/**
* Validate DSL syntax using real WASM compiler
*/
async validateSource(sourceCode) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const result = this.compiler.validate_syntax(sourceCode);
return JSON.parse(result);
}
catch (error) {
throw this.createCompilerError("Syntax validation failed", error);
}
}
/**
* Optimize bytecode using real WASM optimizer
*/
async optimizeBytecode(bytecode) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const optimized = this.compiler.optimize_bytecode(bytecode);
return new Uint8Array(optimized);
}
catch (error) {
throw this.createCompilerError("Bytecode optimization failed", error);
}
}
/**
* Analyze bytecode using real WASM analyzer
*/
async analyzeBytecode(bytecode) {
if (!this.initialized) {
throw this.createCompilerError("Compiler not initialized");
}
try {
// Use semantic analysis for detailed information
const analysis = BytecodeAnalyzer.analyze_semantic(bytecode);
return JSON.parse(analysis);
}
catch (error) {
throw this.createCompilerError("Bytecode analysis failed", error);
}
}
/**
* Get detailed instruction analysis at specific offset
*/
async analyzeInstructionAt(bytecode, offset) {
if (!this.initialized) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const instruction = BytecodeAnalyzer.analyze_instruction_at(bytecode, offset);
return JSON.parse(instruction);
}
catch (error) {
throw this.createCompilerError("Instruction analysis failed", error);
}
}
/**
* Get bytecode summary statistics
*/
async getBytecodeStats(bytecode) {
if (!this.initialized) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const stats = BytecodeAnalyzer.get_bytecode_summary(bytecode);
return JSON.parse(stats);
}
catch (error) {
throw this.createCompilerError("Bytecode stats failed", error);
}
}
/**
* Extract account definitions from DSL source
*/
async extractAccountDefinitions(sourceCode) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const definitions = this.compiler.extract_account_definitions(sourceCode);
return JSON.parse(definitions);
}
catch (error) {
throw this.createCompilerError("Account definition extraction failed", error);
}
}
/**
* Extract function signatures from DSL source
*/
async extractFunctionSignatures(sourceCode) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const signatures = this.compiler.extract_function_signatures(sourceCode);
return JSON.parse(signatures);
}
catch (error) {
throw this.createCompilerError("Function signature extraction failed", error);
}
}
/**
* Validate account constraints
*/
async validateAccountConstraints(sourceCode, functionName, accounts) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const accountsJson = JSON.stringify(accounts);
const validation = this.compiler.validate_account_constraints(sourceCode, functionName, accountsJson);
return JSON.parse(validation);
}
catch (error) {
throw this.createCompilerError("Account constraint validation failed", error);
}
}
/**
* Get compiler version and capabilities from real WASM
*/
getCompilerInfo() {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const info = this.compiler.get_compiler_stats();
return JSON.parse(info);
}
catch (error) {
throw this.createCompilerError("Failed to get compiler info", error);
}
}
/**
* Create a standardized compiler error
*/
createCompilerError(message, cause) {
const error = new Error(message);
error.name = "CompilerError";
error.code = "COMPILER_ERROR";
error.category = "wasm";
error.exitCode = 1;
if (cause) {
error.details = {
cause: cause.message,
stack: cause.stack,
};
}
return error;
}
/**
* Get opcode usage statistics from compilation
*/
async getOpcodeUsage(sourceCode) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const usage = this.compiler.get_opcode_usage(sourceCode);
return JSON.parse(usage);
}
catch (error) {
throw this.createCompilerError("Opcode usage analysis failed", error);
}
}
/**
* Get comprehensive opcode analysis showing used vs unused opcodes
*/
async getOpcodeAnalysis(sourceCode) {
if (!this.initialized || !this.compiler) {
throw this.createCompilerError("Compiler not initialized");
}
try {
const analysis = this.compiler.get_opcode_analysis(sourceCode);
return JSON.parse(analysis);
}
catch (error) {
throw this.createCompilerError("Comprehensive opcode analysis failed", error);
}
}
/**
* Check if compiler is ready
*/
isReady() {
return this.initialized && this.compiler !== null;
}
}
//# sourceMappingURL=compiler.js.map