Page MenuHomePhorge

jit_method_inlining_stress.js
No OneTemporary

Size
5 KB
Referenced Files
None
Subscribers
None

jit_method_inlining_stress.js

function fail(label, expected, actual) {
throw new Error(label + ": expected " + expected + ", got " + actual);
}
function assertEq(actual, expected, label) {
if (actual !== expected) fail(label, expected, actual);
}
function warm(fn, n) {
for (let i = 0; i < (n || 300); i++) fn(i);
}
// Basic direct-slot load/add/store through a prototype method.
function Seq(start) {
this.item = start;
}
Seq.prototype.next = function() {
const old = this.item;
this.item = old + 2;
return old;
};
function runSeqLoop(n) {
const seq = new Seq(1);
let out = 0;
for (let i = 0; i < n; i++) out = seq.next();
return out + seq.item;
}
for (let i = 0; i < 200; i++) runSeqLoop(8);
assertEq(runSeqLoop(1000), 4000, "hot loop inline result");
function callSeq(seq) {
return seq.next();
}
const seq = new Seq(1);
warm(function() { callSeq(seq); });
assertEq(callSeq(seq), 601, "basic inline result");
assertEq(seq.item, 603, "basic inline store");
// Same call site, different receiver shape and different property slot order.
function SlotA() {
this.item = 1;
}
SlotA.prototype.next = Seq.prototype.next;
function callSlotA(obj) {
return obj.next();
}
const slotA = new SlotA();
warm(function() { callSlotA(slotA); });
const slotB = { padding: 99, item: 10 };
Object.setPrototypeOf(slotB, SlotA.prototype);
assertEq(callSlotA(slotB), 10, "receiver shape fallback result");
assertEq(slotB.item, 12, "receiver shape fallback store");
assertEq(slotB.padding, 99, "receiver shape fallback preserves other slot");
// Prototype method identity changes after the call site has warmed.
function Replaceable(start) {
this.item = start;
}
Replaceable.prototype.next = function() {
const old = this.item;
this.item = old + 1;
return old;
};
function callReplaceable(obj) {
return obj.next();
}
const replaceable = new Replaceable(5);
warm(function() { callReplaceable(replaceable); });
const replaceableBefore = replaceable.item;
Replaceable.prototype.next = function() {
return this.item + 1000;
};
assertEq(callReplaceable(replaceable), replaceableBefore + 1000, "prototype method replacement");
assertEq(replaceable.item, replaceableBefore, "prototype replacement does not run old body");
// Own method shadowing changes the receiver shape and callee identity.
function Shadowed(start) {
this.item = start;
}
Shadowed.prototype.next = Seq.prototype.next;
function callShadowed(obj) {
return obj.next();
}
const shadowed = new Shadowed(7);
warm(function() { callShadowed(shadowed); });
shadowed.next = function() {
return this.item * 10;
};
assertEq(callShadowed(shadowed), shadowed.item * 10, "own method shadow fallback");
// Accessor conversion after warmup must not use stale direct slot metadata.
function AccessorBox(start) {
this.item = start;
}
AccessorBox.prototype.next = Seq.prototype.next;
function callAccessor(obj) {
return obj.next();
}
const accessor = new AccessorBox(3);
warm(function() { callAccessor(accessor); });
let backing = 20;
Object.defineProperty(accessor, "item", {
get() { return backing; },
set(v) { backing = v + 100; },
configurable: true
});
assertEq(callAccessor(accessor), 20, "accessor fallback getter result");
assertEq(backing, 122, "accessor fallback setter result");
// A callee with a side effect before a later shape-dependent read should not be
// inlined; otherwise a late guard failure would rerun the method and duplicate
// the side effect.
function LateGuardBox() {
this.count = 0;
}
LateGuardBox.prototype.sideThenRead = function(other) {
this.count = this.count + 1;
return other.value;
};
function callLateGuard(box, other) {
return box.sideThenRead(other);
}
const late = new LateGuardBox();
const monoOther = { value: 1 };
warm(function() { callLateGuard(late, monoOther); });
const lateBefore = late.count;
const otherShape = { padding: 0, value: 7 };
assertEq(callLateGuard(late, otherShape), 7, "late guard fallback result");
assertEq(late.count, lateBefore + 1, "late guard does not duplicate side effect");
// Argument mapping and branch returns in an inlineable method body.
function Accum(start) {
this.item = start;
}
Accum.prototype.addScaled = function(delta, scale) {
if (delta < 0) return this.item;
const old = this.item;
this.item = old + delta * scale;
return this.item;
};
function callAddScaled(obj, delta, scale) {
return obj.addScaled(delta, scale);
}
const accum = new Accum(2);
warm(function() { callAddScaled(accum, 1, 3); });
const accumBefore = accum.item;
assertEq(callAddScaled(accum, 2, 5), accumBefore + 10, "argument mapping result");
assertEq(callAddScaled(accum, -1, 5), accumBefore + 10, "branch return result");
// Class method path, including class-created prototype methods.
class ClassSeq {
constructor(start) {
this.item = start;
}
next() {
const old = this.item;
this.item = old + 4;
return old;
}
}
function callClassSeq(obj) {
return obj.next();
}
const classSeq = new ClassSeq(11);
warm(function() { callClassSeq(classSeq); });
assertEq(callClassSeq(classSeq), 1211, "class inline result");
assertEq(classSeq.item, 1215, "class inline store");
console.log("jit method inlining stress: ok");

File Metadata

Mime Type
application/javascript
Expires
Fri, May 1, 10:17 PM (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
543619
Default Alt Text
jit_method_inlining_stress.js (5 KB)

Event Timeline