function StackManager(rest_interface) {
var frames = this.frames = [];
// Return one particular frame
this.frame = function(i) {
return frames[i];
};
// Reload one particular frame
this.reloadFrame = function(i) {
var d = $.Deferred();
rest_interface.stackFrame(i)
.done(function(frame) {
frames[i] = frame;
d.resolve(frame);
});
return d.promise();
};
var _insert_frame_from_new = function(frameno, frame, frames) {
frame.frameno = frameno;
var frame_obj = new StackFrame(frame);
frames[frameno] = frame_obj;
return frame_obj;
};
// Reload all frames or initialize from new
this.initialize = function() {
var d = $.Deferred();
frames.splice(0, frames.length); // Clear the list
rest_interface.stack()
.done(function(stack) {
for(var i = 0; i < stack.length; i++) {
var frame_obj = _insert_frame_from_new(i, stack[i], frames);
d.notify(frame_obj, null, i);
}
rest_interface.status({next_statement: 1})
.done(function(status) {
d.resolve(status);
})
});
return d.promise();
};
// Update our representation of the stack efficiently using HEAD requests
// when possible and GET requests when neessary
this.update = function() {
var d = $.Deferred(),
prev_frames_by_serial = {};
for (var i = 0; i < frames.length; i++) {
var serial = frames[i].serial;
prev_frames_by_serial[serial] = frames[i];
}
var new_frames = [];
var finalize = function() {
// Got responses from all the stack frames we had before.
// Anything else left in prev_frames_by_serial are gone
for (var serial in prev_frames_by_serial) {
d.notify(prev_frames_by_serial[serial], prev_frames_by_serial[serial].frameno, null);
}
frames = new_frames;
d.resolve();
};
function update_one_frame(frame, i) {
var this_frame_serial = frame.serial;
frame.frameno = i;
new_frames[i] = frame;
if (this_frame_serial in prev_frames_by_serial) {
// this frame existed before
var prev_frame = prev_frames_by_serial[this_frame_serial];
if ((prev_frame.frameno !== frame.frameno)
|| (prev_frame.line !== frame.line)
) {
d.notify(frame, prev_frame.frameno, i); // Only notify if something changed
}
delete prev_frames_by_serial[this_frame_serial];
} else {
// This is a new frame that did not exist before
d.notify(frame, null, i);
}
};
rest_interface.stackNoArgs()
.fail(function(jqxhr, text_status, error_thrown) {
alert('Error getting stack: '+error_thrown);
})
.done(function(stackframes) {
for (var i = (stackframes.length - 1); i >= 0; i--) {
var frame = new StackFrame(stackframes[i]);
update_one_frame(frame, i);
};
finalize();
});
return d.promise();
};
this.updateFrameWithArgs = function(frame) {
var frameno = frame.frameno,
d = $.Deferred();
rest_interface.stackFrame(frameno)
.fail(d)
.done(function(new_frame_data) {
var new_frame = new StackFrame(new_frame_data);
frame.args = new_frame.args;
d.resolve(frame);
});
return d.promise();
};
}
function StackFrame(params) {
var frame = this;
['package','filename','line','subroutine','subname','hasargs',
'wantarray', 'evaltext', 'is_require','hints','bitmask','href',
'evalfile','evalline','autoload','level','serial','frameno'
].forEach(function(key) {
frame[key] = params[key];
});
var args = [];
this.args = args;
if(params.args) {
for(var i = 0; i < params.args.length; i++) {
args[i] = PerlValue.parseFromEval(params.args[i]);
}
}
if (this.wantarray === null) {
this.sigil = '';
} else if (this.wantarray) {
this.sigil = '@';
} else {
this.sigil = '$';
}
}