about summary refs log tree commit diff stats
path: root/js/baba-yaga/src/core/ast-pool.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/baba-yaga/src/core/ast-pool.js')
-rw-r--r--js/baba-yaga/src/core/ast-pool.js526
1 files changed, 526 insertions, 0 deletions
diff --git a/js/baba-yaga/src/core/ast-pool.js b/js/baba-yaga/src/core/ast-pool.js
new file mode 100644
index 0000000..0569c6c
--- /dev/null
+++ b/js/baba-yaga/src/core/ast-pool.js
@@ -0,0 +1,526 @@
+// ast-pool.js - Object pooling for AST nodes to reduce GC pressure
+
+/**
+ * Object pool for AST nodes to reduce garbage collection overhead
+ * Provides significant performance improvement for large programs
+ */
+
+export class ASTNodePool {
+  constructor() {
+    // Pools for different node types
+    this.pools = new Map();
+    
+    // Pool configuration
+    this.maxPoolSize = 1000; // Maximum objects per pool
+    this.initialPoolSize = 50; // Initial objects to create
+    
+    // Statistics
+    this.stats = {
+      created: 0,
+      reused: 0,
+      returned: 0,
+      poolHits: 0,
+      poolMisses: 0
+    };
+
+    // Initialize common node type pools
+    this.initializePools();
+  }
+
+  /**
+   * Initialize pools for common AST node types
+   */
+  initializePools() {
+    const commonTypes = [
+      'BinaryExpression',
+      'UnaryExpression', 
+      'FunctionCall',
+      'Identifier',
+      'NumberLiteral',
+      'StringLiteral',
+      'BooleanLiteral',
+      'ListLiteral',
+      'TableLiteral',
+      'MemberExpression',
+      'WhenExpression',
+      'WhenCase',
+      'AnonymousFunction'
+    ];
+
+    for (const type of commonTypes) {
+      this.pools.set(type, []);
+      
+      // Pre-populate with initial objects
+      for (let i = 0; i < this.initialPoolSize; i++) {
+        this.pools.get(type).push(this.createFreshNode(type));
+      }
+    }
+  }
+
+  /**
+   * Create a fresh AST node of the specified type
+   */
+  createFreshNode(type) {
+    const node = { type };
+    
+    // Initialize common properties based on node type
+    switch (type) {
+      case 'BinaryExpression':
+        node.operator = null;
+        node.left = null;
+        node.right = null;
+        break;
+        
+      case 'UnaryExpression':
+        node.operator = null;
+        node.operand = null;
+        break;
+        
+      case 'FunctionCall':
+        node.callee = null;
+        node.arguments = [];
+        break;
+        
+      case 'Identifier':
+        node.name = null;
+        break;
+        
+      case 'NumberLiteral':
+        node.value = 0;
+        node.isFloat = false;
+        break;
+        
+      case 'StringLiteral':
+        node.value = '';
+        break;
+        
+      case 'BooleanLiteral':
+        node.value = false;
+        break;
+        
+      case 'ListLiteral':
+        node.elements = [];
+        break;
+        
+      case 'TableLiteral':
+        node.properties = [];
+        break;
+        
+      case 'MemberExpression':
+        node.object = null;
+        node.property = null;
+        break;
+        
+      case 'WhenExpression':
+        node.discriminants = [];
+        node.cases = [];
+        break;
+        
+      case 'WhenCase':
+        node.patterns = [];
+        node.consequent = null;
+        break;
+        
+      case 'AnonymousFunction':
+        node.params = [];
+        node.body = null;
+        break;
+    }
+    
+    this.stats.created++;
+    return node;
+  }
+
+  /**
+   * Get a node from the pool or create a new one
+   */
+  acquire(type, properties = {}) {
+    const pool = this.pools.get(type);
+    let node;
+    
+    if (pool && pool.length > 0) {
+      node = pool.pop();
+      this.stats.reused++;
+      this.stats.poolHits++;
+    } else {
+      node = this.createFreshNode(type);
+      this.stats.poolMisses++;
+    }
+    
+    // Apply provided properties
+    Object.assign(node, properties);
+    
+    return node;
+  }
+
+  /**
+   * Return a node to the pool for reuse
+   */
+  release(node) {
+    if (!node || !node.type) {
+      return;
+    }
+    
+    const pool = this.pools.get(node.type);
+    if (!pool) {
+      // Create pool for unknown type
+      this.pools.set(node.type, []);
+      this.pools.get(node.type).push(node);
+      this.stats.returned++;
+      return;
+    }
+    
+    // Don't exceed maximum pool size
+    if (pool.length >= this.maxPoolSize) {
+      return;
+    }
+    
+    // Reset node properties
+    this.resetNode(node);
+    
+    pool.push(node);
+    this.stats.returned++;
+  }
+
+  /**
+   * Reset a node to its initial state
+   */
+  resetNode(node) {
+    const type = node.type;
+    
+    // Clear all properties except type
+    for (const key of Object.keys(node)) {
+      if (key !== 'type') {
+        delete node[key];
+      }
+    }
+    
+    // Reinitialize based on type
+    switch (type) {
+      case 'BinaryExpression':
+        node.operator = null;
+        node.left = null;
+        node.right = null;
+        break;
+        
+      case 'UnaryExpression':
+        node.operator = null;
+        node.operand = null;
+        break;
+        
+      case 'FunctionCall':
+        node.callee = null;
+        node.arguments = [];
+        break;
+        
+      case 'Identifier':
+        node.name = null;
+        break;
+        
+      case 'NumberLiteral':
+        node.value = 0;
+        node.isFloat = false;
+        break;
+        
+      case 'StringLiteral':
+        node.value = '';
+        break;
+        
+      case 'BooleanLiteral':
+        node.value = false;
+        break;
+        
+      case 'ListLiteral':
+        node.elements = [];
+        break;
+        
+      case 'TableLiteral':
+        node.properties = [];
+        break;
+        
+      case 'MemberExpression':
+        node.object = null;
+        node.property = null;
+        break;
+        
+      case 'WhenExpression':
+        node.discriminants = [];
+        node.cases = [];
+        break;
+        
+      case 'WhenCase':
+        node.patterns = [];
+        node.consequent = null;
+        break;
+        
+      case 'AnonymousFunction':
+        node.params = [];
+        node.body = null;
+        break;
+    }
+  }
+
+  /**
+   * Recursively release an entire AST tree
+   */
+  releaseTree(node) {
+    if (!node || typeof node !== 'object') {
+      return;
+    }
+    
+    // Release child nodes first
+    this.visitChildren(node, (child) => {
+      this.releaseTree(child);
+    });
+    
+    // Release this node
+    this.release(node);
+  }
+
+  /**
+   * Visit all child nodes of an AST node
+   */
+  visitChildren(node, callback) {
+    if (!node || typeof node !== 'object') {
+      return;
+    }
+    
+    switch (node.type) {
+      case 'BinaryExpression':
+        if (node.left) callback(node.left);
+        if (node.right) callback(node.right);
+        break;
+        
+      case 'UnaryExpression':
+        if (node.operand) callback(node.operand);
+        break;
+        
+      case 'FunctionCall':
+        if (node.callee) callback(node.callee);
+        if (node.arguments) {
+          for (const arg of node.arguments) {
+            callback(arg);
+          }
+        }
+        break;
+        
+      case 'ListLiteral':
+        if (node.elements) {
+          for (const element of node.elements) {
+            callback(element);
+          }
+        }
+        break;
+        
+      case 'TableLiteral':
+        if (node.properties) {
+          for (const prop of node.properties) {
+            if (prop.value) callback(prop.value);
+          }
+        }
+        break;
+        
+      case 'MemberExpression':
+        if (node.object) callback(node.object);
+        if (node.property) callback(node.property);
+        break;
+        
+      case 'WhenExpression':
+        if (node.discriminants) {
+          for (const discriminant of node.discriminants) {
+            callback(discriminant);
+          }
+        }
+        if (node.cases) {
+          for (const whenCase of node.cases) {
+            callback(whenCase);
+          }
+        }
+        break;
+        
+      case 'WhenCase':
+        if (node.patterns) {
+          for (const pattern of node.patterns) {
+            callback(pattern);
+          }
+        }
+        if (node.consequent) callback(node.consequent);
+        break;
+        
+      case 'AnonymousFunction':
+        if (node.body) callback(node.body);
+        break;
+        
+      case 'Program':
+        if (node.body) {
+          for (const statement of node.body) {
+            callback(statement);
+          }
+        }
+        break;
+    }
+  }
+
+  /**
+   * Get pool statistics
+   */
+  getStats() {
+    const totalRequests = this.stats.poolHits + this.stats.poolMisses;
+    const hitRate = totalRequests > 0 ? this.stats.poolHits / totalRequests : 0;
+    const reuseRate = this.stats.created > 0 ? this.stats.reused / this.stats.created : 0;
+    
+    const poolSizes = {};
+    for (const [type, pool] of this.pools.entries()) {
+      poolSizes[type] = pool.length;
+    }
+    
+    return {
+      ...this.stats,
+      hitRate,
+      reuseRate,
+      poolCount: this.pools.size,
+      poolSizes,
+      totalPooledObjects: Array.from(this.pools.values()).reduce((sum, pool) => sum + pool.length, 0)
+    };
+  }
+
+  /**
+   * Reset all statistics
+   */
+  resetStats() {
+    this.stats = {
+      created: 0,
+      reused: 0,
+      returned: 0,
+      poolHits: 0,
+      poolMisses: 0
+    };
+  }
+
+  /**
+   * Clear all pools and reset
+   */
+  clear() {
+    this.pools.clear();
+    this.resetStats();
+    this.initializePools();
+  }
+
+  /**
+   * Warm up pools by pre-creating objects
+   */
+  warmUp(type, count = 100) {
+    if (!this.pools.has(type)) {
+      this.pools.set(type, []);
+    }
+    
+    const pool = this.pools.get(type);
+    for (let i = 0; i < count; i++) {
+      if (pool.length < this.maxPoolSize) {
+        pool.push(this.createFreshNode(type));
+      }
+    }
+  }
+}
+
+/**
+ * Global AST node pool instance
+ */
+export const globalASTPool = new ASTNodePool();
+
+/**
+ * Convenience functions for common operations
+ */
+export const pooledNodes = {
+  binaryExpression: (operator, left, right) => 
+    globalASTPool.acquire('BinaryExpression', { operator, left, right }),
+    
+  unaryExpression: (operator, operand) =>
+    globalASTPool.acquire('UnaryExpression', { operator, operand }),
+    
+  functionCall: (callee, args) =>
+    globalASTPool.acquire('FunctionCall', { callee, arguments: args }),
+    
+  identifier: (name) =>
+    globalASTPool.acquire('Identifier', { name }),
+    
+  numberLiteral: (value, isFloat = false) =>
+    globalASTPool.acquire('NumberLiteral', { value, isFloat }),
+    
+  stringLiteral: (value) =>
+    globalASTPool.acquire('StringLiteral', { value }),
+    
+  booleanLiteral: (value) =>
+    globalASTPool.acquire('BooleanLiteral', { value }),
+    
+  listLiteral: (elements) =>
+    globalASTPool.acquire('ListLiteral', { elements }),
+    
+  tableLiteral: (properties) =>
+    globalASTPool.acquire('TableLiteral', { properties }),
+    
+  memberExpression: (object, property) =>
+    globalASTPool.acquire('MemberExpression', { object, property }),
+    
+  whenExpression: (discriminants, cases) =>
+    globalASTPool.acquire('WhenExpression', { discriminants, cases }),
+    
+  anonymousFunction: (params, body) =>
+    globalASTPool.acquire('AnonymousFunction', { params, body })
+};
+
+/**
+ * Benchmark AST node pool performance
+ */
+export async function benchmarkASTPool(iterations = 100000) {
+  console.log(`Benchmarking AST node pool with ${iterations} iterations...`);
+  
+  // Benchmark without pooling
+  const nPoolStart = performance.now();
+  const nodes1 = [];
+  
+  for (let i = 0; i < iterations; i++) {
+    nodes1.push({
+      type: 'BinaryExpression',
+      operator: '+',
+      left: { type: 'NumberLiteral', value: i },
+      right: { type: 'NumberLiteral', value: i + 1 }
+    });
+  }
+  
+  const nPoolTime = performance.now() - nPoolStart;
+  
+  // Benchmark with pooling
+  const pool = new ASTNodePool();
+  const poolStart = performance.now();
+  const nodes2 = [];
+  
+  for (let i = 0; i < iterations; i++) {
+    const left = pool.acquire('NumberLiteral', { value: i });
+    const right = pool.acquire('NumberLiteral', { value: i + 1 });
+    const expr = pool.acquire('BinaryExpression', { operator: '+', left, right });
+    nodes2.push(expr);
+  }
+  
+  // Return nodes to pool
+  for (const node of nodes2) {
+    pool.releaseTree(node);
+  }
+  
+  const poolTime = performance.now() - poolStart;
+  
+  console.log(`Without pooling: ${nPoolTime.toFixed(2)}ms`);
+  console.log(`With pooling: ${poolTime.toFixed(2)}ms`);
+  console.log(`Speedup: ${(nPoolTime / poolTime).toFixed(2)}x`);
+  
+  const stats = pool.getStats();
+  console.log(`Pool hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
+  console.log(`Reuse rate: ${(stats.reuseRate * 100).toFixed(1)}%`);
+  
+  return {
+    nPoolTime,
+    poolTime,
+    speedup: nPoolTime / poolTime,
+    stats
+  };
+}