UNPKG

@five-vm/cli

Version:

High-performance CLI for Five VM development with WebAssembly integration

624 lines 26.8 kB
/** * 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