count_ and foam.USEDThis document explains two important debugging mechanisms in FOAM3 for tracking class usage and object instantiation in JS.
count_ - Instance Creation CounterEvery FOAM class has a count_ property that tracks how many instances of that class have been created.
In foam/lang/Boot.js:146:
cls.count_ = 0; // Number of instances created
Every time create() is called on a class, the counter is incremented in foam/lang/FObject.js:63:
function create(args, opt_parent) {
var obj = Object.create(this.prototype);
// Increment number of objects created of this class.
this.count_++;
// ... rest of object initialization
}
// Check how many instances of a specific class were created
console.log(foam.u2.Element.count_); // e.g., 1523
// Check creation count for any class
var myClass = foam.lookup('com.example.MyModel');
console.log(myClass.id, myClass.count_);
foam.USED - Registry of Used Classesfoam.USED is an object (dictionary/map) that stores references to all FOAM models whose class has been built in the current session: the first lookup, requires or extends of a class builds it, even if no instance is ever created. Refinements are added as soon as the class they refine is defined. Classes defined during boot, before foam.USED exists (such as foam.lang.FObject), are not listed.
In foam/lang/EndBoot.js:295:
foam.USED = {};
foam.UNUSED = {};
foam.USED?When a class is first looked up (and so built), it moves from foam.UNUSED to foam.USED:
// From EndBoot.js:337-346
var f = foam.Function.memoize0(function() {
delete foam.UNUSED[m.id];
try {
var c = CLASS(m);
foam.USED[m.id] = m; // <-- Added here when class is first used
return c;
} catch(x) {
console.log('ERROR: Class definition error in', m.id, x);
throw x;
}
});
// Get all used class IDs
Object.keys(foam.USED); // ['foam.u2.Element', ...]
// Check if a specific class has been used
if (foam.USED['com.example.MyModel']) {
console.log('MyModel has been used');
}
// Get the model definition
var modelDef = foam.USED['com.example.MyModel'];
The value is the raw spec, not a built model. foam.USED[id] holds the plain object literal that was passed to foam.CLASS (src/foam/lang/EndBoot.js:322, :341), before any adapt ran. So mixins, requires and properties are still in their shorthand forms: a mixin is the string 'foam.u2.memento.Memorable', not a foam.lang.Mixin with a path (that object is only made when the class is built, src/foam/lang/Mixin.js:44-52). A filter on mx.path silently matches nothing:
// always [] : mixins are strings here
Object.values(foam.USED).filter(m => (m.mixins || []).some(mx => mx.path === id));
// works for both shapes
Object.values(foam.USED).filter(m => (m.mixins || []).some(mx => (mx.path || mx) === id));
For built metadata, look the class up and read its model: foam.lookup(id).model_.
The most common debugging use case combines both to analyze memory usage and object creation patterns.
// Show all classes and their instance counts
Object.keys(foam.USED).forEach(k => {
try {
var m = foam.maybeLookup(k);
console.log(m.id, m.count_);
} catch (x) {}
});
FOAM3 includes pre-built scripts in scripts.jrl:
Show Creation Counts - Lists all classes with their instance counts:
var a = [];
Object.keys(foam.USED).forEach(k => {
try {
var m = foam.maybeLookup(k);
if ( m && m.count_ ) a.push(m);
} catch (x) {}
});
a.sort(function (a, b) { return a.count_ - b.count_ });
for ( var i = 0 ; i < a.length ; i++ ) {
var m = a[i];
console.log(m.id, m.count_);
}
Clear Creation Counts - Resets all counters to zero (useful for measuring specific operations):
Object.keys(foam.USED).forEach(k => {
try {
var m = foam.maybeLookup(k);
if ( m && m.count_ ) m.count_ = 0;
} catch (x) {}
});
foam.UNUSEDfoam.UNUSED contains models that are defined but whose class has not been built yet. When a class is first looked up, it moves from foam.UNUSED to foam.USED.
// Check what classes are defined but never looked up
Object.keys(foam.UNUSED);
If count_ keeps growing for a class that should have limited instances, you may have a leak:
// Before operation
var before = foam.lookup('MyClass').count_;
// ... perform operation ...
// After operation
var after = foam.lookup('MyClass').count_;
console.log('Created:', after - before, 'instances');
// Clear counts
Object.keys(foam.USED).forEach(k => {
try { foam.maybeLookup(k).count_ = 0; } catch(x) {}
});
// Perform action you want to profile
await someOperation();
// Show what was created
var results = [];
Object.keys(foam.USED).forEach(k => {
var m = foam.maybeLookup(k);
if (m && m.count_ > 0) results.push({ id: m.id, count: m.count_ });
});
results.sort((a,b) => b.count - a.count);
console.table(results.slice(0, 20)); // Top 20 most created classes
See which classes are used when a feature is triggered:
var usedBefore = new Set(Object.keys(foam.USED));
// Trigger feature
clickButton();
var newlyUsed = Object.keys(foam.USED).filter(k => !usedBefore.has(k));
console.log('Newly loaded classes:', newlyUsed);