зеркало из https://github.com/mozilla/pluotsorbet.git
584 строки
17 KiB
JavaScript
584 строки
17 KiB
JavaScript
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
|
|
'use strict';
|
|
|
|
// The real profile and release variable declaration in config.ts are folded away by closure. Until we
|
|
// make closure process this file also, make sure that |profile| is defined in this file.
|
|
var release;
|
|
var profile;
|
|
|
|
var jvm = new JVM();
|
|
|
|
if ("gamepad" in config && !/no|0/.test(config.gamepad)) {
|
|
document.documentElement.classList.add('gamepad');
|
|
}
|
|
|
|
var jars = [];
|
|
|
|
if (typeof Benchmark !== "undefined") {
|
|
Benchmark.startup.init();
|
|
}
|
|
|
|
if (config.jars) {
|
|
jars = jars.concat(config.jars.split(":"));
|
|
}
|
|
|
|
// Mobile info gets accessed a lot, so we cache it on startup.
|
|
var mobileInfo;
|
|
var getMobileInfo = new Promise(function(resolve, reject) {
|
|
var sender = DumbPipe.open("mobileInfo", {}, function(message) {
|
|
mobileInfo = message;
|
|
DumbPipe.close(sender);
|
|
resolve();
|
|
});
|
|
});
|
|
|
|
var loadingMIDletPromises = [initFS, getMobileInfo];
|
|
|
|
var loadingPromises = [];
|
|
|
|
loadingPromises.push(load("java/classes.jar", "arraybuffer").then(function(data) {
|
|
JARStore.addBuiltIn("java/classes.jar", data);
|
|
CLASSES.initializeBuiltinClasses();
|
|
}));
|
|
|
|
jars.forEach(function(jar) {
|
|
loadingMIDletPromises.push(load(jar, "arraybuffer").then(function(data) {
|
|
JARStore.addBuiltIn(jar, data);
|
|
}));
|
|
});
|
|
|
|
function processJAD(data) {
|
|
data
|
|
.replace(/\r\n|\r/g, "\n")
|
|
.replace(/\n /g, "")
|
|
.split("\n")
|
|
.forEach(function(entry) {
|
|
if (entry) {
|
|
var keyEnd = entry.indexOf(":");
|
|
var key = entry.substring(0, keyEnd);
|
|
var val = entry.substring(keyEnd + 1).trim();
|
|
MIDP.manifest[key] = val;
|
|
}
|
|
});
|
|
}
|
|
|
|
if (config.jad) {
|
|
loadingMIDletPromises.push(load(config.jad, "text").then(processJAD).then(backgroundCheck));
|
|
}
|
|
|
|
function performDownload(url, callback) {
|
|
showDownloadScreen();
|
|
var progressBar = downloadDialog.querySelector('progress.pack-activity');
|
|
|
|
var sender = DumbPipe.open("JARDownloader", url, function(message) {
|
|
switch (message.type) {
|
|
case "done":
|
|
DumbPipe.close(sender);
|
|
hideDownloadScreen();
|
|
progressBar.value = 0;
|
|
callback(message.data);
|
|
|
|
break;
|
|
|
|
case "progress":
|
|
progressBar.value = message.progress;
|
|
break;
|
|
|
|
case "fail":
|
|
DumbPipe.close(sender);
|
|
hideDownloadScreen();
|
|
progressBar.value = 0;
|
|
|
|
var failureDialog = document.getElementById('download-failure-dialog');
|
|
failureDialog.style.display = '';
|
|
|
|
var btnRetry = failureDialog.querySelector('button.recommend');
|
|
btnRetry.addEventListener('click', function onclick(e) {
|
|
e.preventDefault();
|
|
btnRetry.removeEventListener('click', onclick);
|
|
failureDialog.style.display = 'none';
|
|
performDownload(url, callback);
|
|
});
|
|
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
if (config.downloadJAD) {
|
|
loadingMIDletPromises.push(new Promise(function(resolve, reject) {
|
|
JARStore.loadJAR("midlet.jar").then(function(loaded) {
|
|
if (loaded) {
|
|
// Show the splash screen as soon as possible.
|
|
showSplashScreen();
|
|
|
|
processJAD(JARStore.getJAD());
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
performDownload(config.downloadJAD, function(data) {
|
|
// Show the splash screen as soon as possible after showing
|
|
// the download screen while downloading the JAD/JAR files.
|
|
showSplashScreen();
|
|
|
|
JARStore.installJAR("midlet.jar", data.jarData, data.jadData).then(function() {
|
|
processJAD(data.jadData);
|
|
resolve();
|
|
});
|
|
});
|
|
});
|
|
}).then(backgroundCheck));
|
|
}
|
|
|
|
var loadingFGPromises = [ emoji.loadData() ];
|
|
|
|
if (jars.indexOf("tests/tests.jar") !== -1) {
|
|
loadingPromises.push(loadScript("tests/native.js"),
|
|
loadScript("tests/mozactivitymock.unprivileged.js"),
|
|
loadScript("tests/config.js"));
|
|
}
|
|
|
|
function getIsOff(button) {
|
|
return button.textContent.contains("OFF");
|
|
}
|
|
function toggle(button) {
|
|
var isOff = getIsOff(button);
|
|
button.textContent = button.textContent.replace(isOff ? "OFF" : "ON", isOff ? "ON" : "OFF");
|
|
}
|
|
|
|
var bigBang = 0;
|
|
var profiling = false;
|
|
|
|
function startTimeline() {
|
|
jsGlobal.START_TIME = performance.now();
|
|
jsGlobal.profiling = true;
|
|
requestTimelineBuffers(function (buffers) {
|
|
for (var i = 0; i < buffers.length; i++) {
|
|
buffers[i].reset(jsGlobal.START_TIME);
|
|
}
|
|
for (var runtime of J2ME.RuntimeTemplate.all) {
|
|
for (var ctx of runtime.allCtxs) {
|
|
ctx.restartMethodTimeline();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function stopTimeline(cb) {
|
|
jsGlobal.profiling = false;
|
|
requestTimelineBuffers(function(buffers) {
|
|
// Some of the methods may have not exited yet. Leave them
|
|
// so they show up in the profile.
|
|
for (var i = 0; i < buffers.length; i++) {
|
|
while(buffers[i].depth > 0) {
|
|
buffers[i].leave();
|
|
}
|
|
}
|
|
cb(buffers);
|
|
});
|
|
}
|
|
|
|
function stopAndSaveTimeline() {
|
|
console.log("Saving profile, please wait ...");
|
|
var traceFormat = Shumway.Tools.Profiler.TraceFormat[profileFormat.toUpperCase()];
|
|
var output = [];
|
|
var writer = new J2ME.IndentingWriter(false, function (s) {
|
|
output.push(s);
|
|
});
|
|
if (traceFormat === Shumway.Tools.Profiler.TraceFormat.CSV) {
|
|
writer.writeLn("Name,Count,Self (ms),Total (ms)");
|
|
}
|
|
stopTimeline(function (buffers) {
|
|
var snapshots = [];
|
|
for (var i = 0; i < buffers.length; i++) {
|
|
snapshots.push(buffers[i].createSnapshot());
|
|
}
|
|
// Trace Statistics
|
|
for (var i = 0; i < snapshots.length; i++) {
|
|
writer.writeLn("Timeline Statistics: " + snapshots[i].name);
|
|
snapshots[i].traceStatistics(writer, 1, traceFormat); // Don't trace any totals below 1 ms.
|
|
}
|
|
// Trace Aggregate Method Statistics
|
|
writer.writeLn("Timeline Statistics: All Threads");
|
|
var methodSnapshots = snapshots.slice(2);
|
|
new Shumway.Tools.Profiler.TimelineBufferSnapshotSet(methodSnapshots).traceStatistics(writer, 1, traceFormat);
|
|
// Trace Events
|
|
for (var i = 0; i < snapshots.length; i++) {
|
|
writer.writeLn("Timeline Events: " + snapshots[i].name);
|
|
snapshots[i].trace(writer, 0.1); // Don't trace anything below 0.1 ms.
|
|
}
|
|
});
|
|
var text = output.join("\n");
|
|
|
|
var fileExtension, mediaType;
|
|
switch (traceFormat) {
|
|
case Shumway.Tools.Profiler.TraceFormat.CSV:
|
|
fileExtension = "csv";
|
|
mediaType = "text/csv";
|
|
break;
|
|
case Shumway.Tools.Profiler.PLAIN:
|
|
default:
|
|
fileExtension = "txt";
|
|
mediaType = "text/plain";
|
|
break;
|
|
}
|
|
var profileFilename = "profile." + fileExtension;
|
|
var blob = new Blob([text], {type : mediaType});
|
|
saveAs(blob, profileFilename);
|
|
console.log("Saved profile in: adb pull /sdcard/downloads/" + profileFilename);
|
|
}
|
|
|
|
function start() {
|
|
var deferStartup = config.deferStartup | 0;
|
|
if (deferStartup && typeof Benchmark !== "undefined") {
|
|
setTimeout(function () {
|
|
Benchmark.startup.setStartTime(performance.now());
|
|
run();
|
|
}, deferStartup);
|
|
} else {
|
|
run();
|
|
}
|
|
function run() {
|
|
J2ME.Context.setWriters(new J2ME.IndentingWriter());
|
|
// For profile mode 1, we start the profiler and wait 2 seconds and show the flame chart UI.
|
|
profile === 1 && profiler.start(2000, false);
|
|
bigBang = performance.now();
|
|
// For profiler mode 2, we start the timeline and stop it later by calling |stopAndSaveTimeline|.
|
|
profile === 2 && startTimeline();
|
|
jvm.startIsolate0(config.main, config.args);
|
|
}
|
|
}
|
|
|
|
// If we're not running a MIDlet, we need to wait everything to be loaded.
|
|
if (!config.midletClassName) {
|
|
loadingPromises = loadingPromises.concat(loadingMIDletPromises);
|
|
}
|
|
|
|
Promise.all(loadingPromises).then(start, function (reason) {
|
|
console.error("Loading failed: \"" + reason + "\"");
|
|
});
|
|
|
|
document.getElementById("start").onclick = function() {
|
|
start();
|
|
};
|
|
|
|
document.getElementById("canvasSize").onchange = function() {
|
|
Array.prototype.forEach.call(document.body.classList, function(c) {
|
|
if (c.indexOf('size-') == 0) {
|
|
document.body.classList.remove(c);
|
|
}
|
|
});
|
|
|
|
if (this.value) {
|
|
document.body.classList.add(this.value);
|
|
}
|
|
|
|
MIDP.updatePhysicalScreenSize();
|
|
MIDP.updateCanvas();
|
|
start();
|
|
};
|
|
|
|
if (typeof Benchmark !== "undefined") {
|
|
Benchmark.initUI("benchmark");
|
|
}
|
|
|
|
window.onload = function() {
|
|
document.getElementById("deleteDatabases").onclick = function() {
|
|
fs.deleteDatabase().then(function() {
|
|
console.log("Deleted fs database.");
|
|
}).catch(function(error) {
|
|
console.log("Error deleting fs database: " + error);
|
|
});
|
|
|
|
CompiledMethodCache.deleteDatabase().then(function() {
|
|
console.log("Deleted CompiledMethodCache database.");
|
|
}).catch(function(error) {
|
|
console.log("Error deleting CompiledMethodCache database: " + error);
|
|
});
|
|
|
|
JARStore.deleteDatabase().then(function() {
|
|
console.log("Deleted JARStore database.");
|
|
}).catch(function(error) {
|
|
console.log("Error deleting JARStore database: " + error);
|
|
});
|
|
};
|
|
document.getElementById("exportstorage").onclick = function() {
|
|
fs.exportStore(function(blob) {
|
|
saveAs(blob, "fs-" + Date.now() + ".json");
|
|
});
|
|
};
|
|
document.getElementById("importstorage").onclick = function() {
|
|
function performImport(file) {
|
|
fs.importStore(file, function() {
|
|
DumbPipe.close(DumbPipe.open("alert", "Import completed."));
|
|
});
|
|
}
|
|
|
|
var file = document.getElementById("importstoragefile").files[0];
|
|
if (file) {
|
|
performImport(file);
|
|
} else {
|
|
load(document.getElementById("importstorageurl").value, "blob").then(function(blob) {
|
|
performImport(blob);
|
|
});
|
|
}
|
|
};
|
|
document.getElementById("clearCompiledMethodCache").onclick = function() {
|
|
CompiledMethodCache.clear().then(function() { console.log("cleared compiled method cache") });
|
|
};
|
|
document.getElementById("printAllExceptions").onclick = function() {
|
|
VM.DEBUG_PRINT_ALL_EXCEPTIONS = !VM.DEBUG_PRINT_ALL_EXCEPTIONS;
|
|
toggle(this);
|
|
};
|
|
document.getElementById("clearCounters").onclick = function() {
|
|
clearCounters();
|
|
};
|
|
|
|
function numberWithCommas(x) {
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
}
|
|
|
|
setInterval(function () {
|
|
var el = document.getElementById("bytecodeCount");
|
|
el.textContent = numberWithCommas(J2ME.bytecodeCount);
|
|
|
|
var el = document.getElementById("interpreterCount");
|
|
el.textContent = numberWithCommas(J2ME.interpreterCount);
|
|
|
|
var el = document.getElementById("compiledCount");
|
|
el.textContent = numberWithCommas(J2ME.compiledMethodCount) + "/" +
|
|
numberWithCommas(J2ME.cachedMethodCount) + "/" +
|
|
numberWithCommas(J2ME.aotMethodCount) + "/" +
|
|
numberWithCommas(J2ME.notCompiledMethodCount);
|
|
|
|
var el = document.getElementById("onStackReplacementCount");
|
|
el.textContent = numberWithCommas(J2ME.onStackReplacementCount);
|
|
|
|
var el = document.getElementById("unwindCount");
|
|
el.textContent = numberWithCommas(J2ME.unwindCount);
|
|
|
|
var el = document.getElementById("preemptionCount");
|
|
el.textContent = numberWithCommas(J2ME.preemptionCount);
|
|
|
|
}, 500);
|
|
|
|
function dumpCounters() {
|
|
var writer = new J2ME.IndentingWriter();
|
|
|
|
writer.writeLn("Frame Count: " + J2ME.frameCount);
|
|
writer.writeLn("Unwind Count: " + J2ME.unwindCount);
|
|
writer.writeLn("Bytecode Count: " + J2ME.bytecodeCount);
|
|
writer.writeLn("OSR Count: " + J2ME.onStackReplacementCount);
|
|
|
|
if (J2ME.interpreterCounter) {
|
|
writer.enter("interpreterCounter");
|
|
J2ME.interpreterCounter.traceSorted(writer);
|
|
writer.outdent();
|
|
}
|
|
if (J2ME.interpreterMethodCounter) {
|
|
writer.enter("interpreterMethodCounter");
|
|
J2ME.interpreterMethodCounter.traceSorted(writer);
|
|
writer.outdent();
|
|
}
|
|
if (J2ME.baselineMethodCounter) {
|
|
writer.enter("baselineMethodCounter");
|
|
J2ME.baselineMethodCounter.traceSorted(writer);
|
|
writer.outdent();
|
|
}
|
|
if (J2ME.baselineCounter) {
|
|
writer.enter("baselineCounter");
|
|
J2ME.baselineCounter.traceSorted(writer);
|
|
writer.outdent();
|
|
}
|
|
if (J2ME.nativeCounter) {
|
|
writer.enter("nativeCounter");
|
|
J2ME.nativeCounter.traceSorted(writer);
|
|
writer.outdent();
|
|
}
|
|
if (J2ME.runtimeCounter) {
|
|
writer.enter("runtimeCounter");
|
|
J2ME.runtimeCounter.traceSorted(writer);
|
|
writer.outdent();
|
|
}
|
|
if (J2ME.asyncCounter) {
|
|
writer.enter("asyncCounter");
|
|
J2ME.asyncCounter.traceSorted(writer);
|
|
writer.outdent();
|
|
}
|
|
}
|
|
function clearCounters() {
|
|
J2ME.frameCount = 0;
|
|
J2ME.unwindCount = 0;
|
|
J2ME.bytecodeCount = 0;
|
|
J2ME.interpreterCount = 0;
|
|
J2ME.onStackReplacementCount = 0;
|
|
|
|
J2ME.interpreterCounter && J2ME.interpreterCounter.clear();
|
|
J2ME.interpreterMethodCounter && J2ME.interpreterMethodCounter.clear();
|
|
J2ME.nativeCounter && J2ME.nativeCounter.clear();
|
|
J2ME.runtimeCounter && J2ME.runtimeCounter.clear();
|
|
J2ME.asyncCounter && J2ME.asyncCounter.clear();
|
|
J2ME.baselineMethodCounter && J2ME.baselineMethodCounter.clear();
|
|
J2ME.baselineCounter && J2ME.baselineCounter.clear();
|
|
}
|
|
|
|
document.getElementById("dumpCounters").onclick = function() {
|
|
dumpCounters();
|
|
};
|
|
document.getElementById("sampleCounters1").onclick = function() {
|
|
clearCounters();
|
|
dumpCounters();
|
|
setTimeout(function () {
|
|
dumpCounters();
|
|
}, 1000);
|
|
};
|
|
document.getElementById("sampleCounters2").onclick = function() {
|
|
clearCounters();
|
|
function sample() {
|
|
var c = 1;
|
|
function tick() {
|
|
if (c-- > 0) {
|
|
dumpCounters();
|
|
clearCounters();
|
|
setTimeout(tick, 16);
|
|
}
|
|
}
|
|
|
|
setTimeout(tick, 100);
|
|
}
|
|
setTimeout(sample, 2000); // Wait 2s before starting.
|
|
};
|
|
};
|
|
|
|
function requestTimelineBuffers(fn) {
|
|
if (J2ME.timeline) {
|
|
// If you change the position at which method timelines begin in this array,
|
|
// then also update the method timeline aggregation in stopAndSaveTimeline.
|
|
var activeTimeLines = [
|
|
J2ME.threadTimeline,
|
|
J2ME.timeline,
|
|
];
|
|
var methodTimeLines = J2ME.methodTimelines;
|
|
for (var i = 0; i < methodTimeLines.length; i++) {
|
|
activeTimeLines.push(methodTimeLines[i]);
|
|
}
|
|
fn(activeTimeLines);
|
|
return;
|
|
}
|
|
return fn([]);
|
|
}
|
|
|
|
var perfWriterCheckbox = document.querySelector('#perfWriter');
|
|
|
|
perfWriterCheckbox.checked = !!(J2ME.writers & J2ME.WriterFlags.Perf);
|
|
perfWriterCheckbox.addEventListener('change', function() {
|
|
if (perfWriterCheckbox.checked) {
|
|
J2ME.writers |= J2ME.WriterFlags.Perf;
|
|
} else {
|
|
J2ME.writers &= !J2ME.WriterFlags.Perf;
|
|
}
|
|
});
|
|
|
|
var profiler = profile === 1 ? (function() {
|
|
|
|
var elPageContainer = document.getElementById("pageContainer");
|
|
elPageContainer.classList.add("profile-mode");
|
|
|
|
var elProfilerContainer = document.getElementById("profilerContainer");
|
|
var elProfilerToolbar = document.getElementById("profilerToolbar");
|
|
var elProfilerMessage = document.getElementById("profilerMessage");
|
|
var elProfilerPanel = document.getElementById("profilePanel");
|
|
var elBtnStartStop = document.getElementById("profilerStartStop");
|
|
var elBtnAdjustHeight = document.getElementById("profilerAdjustHeight");
|
|
|
|
var controller;
|
|
var startTime;
|
|
var timerHandle;
|
|
var timeoutHandle;
|
|
|
|
var Profiler = function() {
|
|
controller = new Shumway.Tools.Profiler.Controller(elProfilerPanel);
|
|
elBtnStartStop.addEventListener("click", this._onStartStopClick.bind(this));
|
|
elBtnAdjustHeight.addEventListener("click", this._onAdjustHeightClick.bind(this));
|
|
|
|
var self = this;
|
|
window.addEventListener("keypress", function (event) {
|
|
if (event.altKey && event.keyCode === 114) { // Alt + R
|
|
self._onStartStopClick();
|
|
}
|
|
}, false);
|
|
}
|
|
|
|
Profiler.prototype.start = function(maxTime, resetTimelines) {
|
|
startTimeline();
|
|
controller.deactivateProfile();
|
|
maxTime = maxTime || 0;
|
|
elProfilerToolbar.classList.add("withEmphasis");
|
|
elBtnStartStop.textContent = "Stop";
|
|
startTime = Date.now();
|
|
timerHandle = setInterval(showTimeMessage, 1000);
|
|
if (maxTime) {
|
|
timeoutHandle = setTimeout(this.createProfile.bind(this), maxTime);
|
|
}
|
|
showTimeMessage();
|
|
}
|
|
|
|
Profiler.prototype.createProfile = function() {
|
|
stopTimeline(function (buffers) {
|
|
controller.createProfile(buffers);
|
|
elProfilerToolbar.classList.remove("withEmphasis");
|
|
elBtnStartStop.textContent = "Start";
|
|
clearInterval(timerHandle);
|
|
clearTimeout(timeoutHandle);
|
|
timerHandle = 0;
|
|
timeoutHandle = 0;
|
|
showTimeMessage(false);
|
|
});
|
|
}
|
|
|
|
Profiler.prototype.openPanel = function() {
|
|
elProfilerContainer.classList.remove("collapsed");
|
|
}
|
|
|
|
Profiler.prototype.closePanel = function() {
|
|
elProfilerContainer.classList.add("collapsed");
|
|
}
|
|
|
|
Profiler.prototype.resize = function() {
|
|
controller.resize();
|
|
}
|
|
|
|
Profiler.prototype._onAdjustHeightClick = function(e) {
|
|
elProfilerContainer.classList.toggle("max");
|
|
};
|
|
|
|
Profiler.prototype._onMinimizeClick = function(e) {
|
|
if (elProfilerContainer.classList.contains("collapsed")) {
|
|
this.openPanel();
|
|
} else {
|
|
this.closePanel();
|
|
}
|
|
}
|
|
|
|
Profiler.prototype._onStartStopClick = function(e) {
|
|
if (timerHandle) {
|
|
this.createProfile();
|
|
this.openPanel();
|
|
} else {
|
|
this.start(0, true);
|
|
}
|
|
}
|
|
|
|
function showTimeMessage(show) {
|
|
show = typeof show === "undefined" ? true : show;
|
|
var time = Math.round((Date.now() - startTime) / 1000);
|
|
elProfilerMessage.textContent = show ? "Running: " + time + " Seconds" : "";
|
|
}
|
|
|
|
return new Profiler();
|
|
|
|
})() : undefined;
|