diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java index 083deb0b6..217898fef 100644 --- a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java +++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptTestMojo.java @@ -153,9 +153,18 @@ public class BuildJavascriptTestMojo extends AbstractMojo { } final Log log = getLog(); new File(outputDir, "tests").mkdirs(); - resourceToFile("org/teavm/javascript/runtime.js", "runtime.js"); - resourceToFile("org/teavm/maven/junit-support.js", "junit-support.js"); - resourceToFile("org/teavm/maven/junit.css", "junit.css"); + new File(outputDir, "res").mkdirs(); + resourceToFile("org/teavm/javascript/runtime.js", "res/runtime.js"); + resourceToFile("org/teavm/maven/res/junit-support.js", "res/junit-support.js"); + resourceToFile("org/teavm/maven/res/junit.css", "res/junit.css"); + resourceToFile("org/teavm/maven/res/class_obj.png", "res/class_obj.png"); + resourceToFile("org/teavm/maven/res/control-000-small.png", "res/control-000-small.png"); + resourceToFile("org/teavm/maven/res/methpub_obj.png", "res/methpub_obj.png"); + resourceToFile("org/teavm/maven/res/package_obj.png", "res/package_obj.png"); + resourceToFile("org/teavm/maven/res/tick-small-red.png", "res/tick-small-red.png"); + resourceToFile("org/teavm/maven/res/tick-small.png", "res/tick-small.png"); + resourceToFile("org/teavm/maven/res/toggle-small-expand.png", "res/toggle-small-expand.png"); + resourceToFile("org/teavm/maven/res/toggle-small.png", "res/toggle-small.png"); resourceToFile("org/teavm/maven/junit.html", "junit.html"); final ClassHolderSource classSource = new ClasspathClassHolderSource(classLoader); for (String testClass : testClasses) { @@ -170,8 +179,8 @@ public class BuildJavascriptTestMojo extends AbstractMojo { includeAdditionalScripts(classLoader); File allTestsFile = new File(outputDir, "tests/all.js"); try (Writer allTestsWriter = new OutputStreamWriter(new FileOutputStream(allTestsFile), "UTF-8")) { - allTestsWriter.write("doRunTests = function() {\n"); - allTestsWriter.write(" new JUnitServer(document.body).runAllTests(["); + allTestsWriter.write("prepare = function() {\n"); + allTestsWriter.write(" return new JUnitServer(document.body).readTests(["); boolean first = true; for (String testClass : testClasses) { if (!first) { @@ -547,3 +556,4 @@ public class BuildJavascriptTestMojo extends AbstractMojo { } } } + diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit-support.js b/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit-support.js deleted file mode 100644 index 7be876edb..000000000 --- a/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit-support.js +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2013 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -JUnitServer = function(container) { - this.container = container; - this.timeSpent = 0; - this.totalTimeSpent = 0; - this.methodCount = 0; - this.statusCell = null; - this.exceptionCell = null; - this.timeCell = null; - this.startTime = 0; - this.expectedExceptions = []; - this.table = null; - this.tableBody = null; - this.frame = null; -} -JUnitServer.prototype = new Object(); -JUnitServer.prototype.handleEvent = function(message, callback) { - endTime = new Date().getTime(); - if (message.status === "ok") { - if (this.expectedExceptions.length > 0) { - this.statusCell.appendChild(document.createTextNode("expected exception not thrown")); - this.statusCell.style.color = 'yellow'; - } else { - this.statusCell.appendChild(document.createTextNode("ok")); - this.statusCell.style.color = 'green'; - } - } else if (message.status === "exception") { - if (message.exception && this.isExpectedException(message.exception)) { - this.statusCell.appendChild(document.createTextNode("ok")); - this.statusCell.style.color = 'green'; - } else { - this.statusCell.appendChild(document.createTextNode("unexpected exception")); - var exceptionText = document.createElement("pre"); - exceptionText.appendChild(document.createTextNode(message.stack)); - this.exceptionCell.appendChild(exceptionText); - this.statusCell.style.color = 'red'; - } - } - ++this.methodCount; - var timeSpent = (endTime - this.startTime) / 1000; - this.timeSpent += timeSpent; - this.timeCell.appendChild(document.createTextNode(timeSpent.toFixed(3))); - document.body.removeChild(this.frame); - self.frame = null; - callback(); -} -JUnitServer.prototype.isExpectedException = function(ex) { - for (var i = 0; i < this.expectedExceptions.length; ++i) { - if (this.expectedExceptions[i] === ex) { - return true; - } - } - return false; -} -JUnitServer.prototype.runTestCase = function(methodName, path, expectedExceptions, additionalScripts, callback) { - this.createRow(methodName); - this.startTime = new Date().getTime(); - this.expectedExceptions = expectedExceptions; - var self = this; - this.loadCode(path, additionalScripts, function() { - messageHandler = function(event) { - window.removeEventListener("message", messageHandler); - self.handleEvent(JSON.parse(event.data), callback); - }; - window.addEventListener("message", messageHandler); - self.frame.contentWindow.postMessage("runTest", "*"); - }); -} -JUnitServer.prototype.createRow = function(methodName) { - var row = document.createElement("tr"); - this.tableBody.appendChild(row); - var nameCell = document.createElement("td"); - row.appendChild(nameCell); - nameCell.appendChild(document.createTextNode(methodName)); - this.statusCell = document.createElement("td"); - row.appendChild(this.statusCell); - this.exceptionCell = document.createElement("td"); - row.appendChild(this.exceptionCell); - this.timeCell = document.createElement("td"); - row.appendChild(this.timeCell); -} -JUnitServer.prototype.loadCode = function(path, additionalScripts, callback) { - this.frame = document.createElement("iframe"); - document.body.appendChild(this.frame); - var frameDoc = this.frame.contentWindow.document; - var self = this; - var sequence = ["junit-support.js", "runtime.js"]; - if (additionalScripts) { - for (var i = 0; i < additionalScripts.length; ++i) { - sequence.push(additionalScripts[i]); - } - } - sequence.push(path); - self.loadScripts(sequence, 0, callback); -} -JUnitServer.prototype.loadScripts = function(scripts, index, callback) { - var self = this; - if (index == scripts.length) { - callback(); - } else { - this.loadScript(scripts[index], function() { self.loadScripts(scripts, index + 1, callback) }); - } -} -JUnitServer.prototype.loadScript = function(name, callback) { - var doc = this.frame.contentWindow.document; - var script = doc.createElement("script"); - script.src = name; - doc.body.appendChild(script); - script.onload = function() { - callback(); - } -} -JUnitServer.prototype.runTest = function(test, callback) { - this.timeSpent = 0; - this.methodCount = 0; - this.createTable(test.name); - var self = this; - this.runMethodFromList(test.methods, 0, function() { - self.createFooter(); - callback(); - }); -} -JUnitServer.prototype.runAllTests = function(tests, callback) { - this.runTestFromList(tests, 0, callback); -} -JUnitServer.prototype.runTestFromList = function(tests, index, callback) { - if (index < tests.length) { - var test = tests[index]; - var self = this; - this.runTest(test, function() { - self.runTestFromList(tests, index + 1, callback); - }); - } else { - callback(); - } -} -JUnitServer.prototype.runMethodFromList = function(methods, index, callback) { - if (index < methods.length) { - var method = methods[index]; - var self = this; - this.runTestCase(method.name, method.script, method.expected, method.additionalScripts, function() { - self.runMethodFromList(methods, index + 1, callback); - }); - } else { - callback(); - } -} -JUnitServer.prototype.createTable = function(name) { - this.table = document.createElement("table"); - this.container.appendChild(this.table); - var caption = document.createElement("caption"); - this.table.appendChild(caption); - this.createHeader(); - caption.appendChild(document.createTextNode(name)); - this.tableBody = document.createElement("tbody"); - this.table.appendChild(this.tableBody); -} -JUnitServer.prototype.createHeader = function() { - var head = document.createElement("thead"); - this.table.appendChild(head); - var headRow = document.createElement("tr"); - head.appendChild(headRow); - var headCell = document.createElement("th"); - headRow.appendChild(headCell); - headCell.appendChild(document.createTextNode("Method")); - headCell = document.createElement("th"); - headRow.appendChild(headCell); - headCell.appendChild(document.createTextNode("Result")); - headCell = document.createElement("th"); - headRow.appendChild(headCell); - headCell.appendChild(document.createTextNode("Exception")); - headCell = document.createElement("th"); - headRow.appendChild(headCell); - headCell.appendChild(document.createTextNode("Time spent, s")); - this.table.appendChild(head); -} -JUnitServer.prototype.createFooter = function() { - var foot = document.createElement("tfoot"); - this.table.appendChild(foot); - var footRow = document.createElement("tr"); - foot.appendChild(footRow); - var footName = document.createElement("td"); - footRow.appendChild(footName); - footName.appendChild(document.createTextNode("---")); - var footMethods = document.createElement("td"); - footRow.appendChild(footMethods); - footMethods.appendChild(document.createTextNode(this.methodCount)); - var footSpace = document.createElement("td"); - footRow.appendChild(footSpace); - footSpace.appendChild(document.createTextNode("---")); - var footTime = document.createElement("td"); - footRow.appendChild(footTime); - footTime.appendChild(document.createTextNode(this.timeSpent.toFixed(3))); -} - -JUnitClient = {}; -JUnitClient.run = function() { - var handler = window.addEventListener("message", function() { - window.removeEventListener("message", handler); - var message = {}; - try { - var instance = new TestClass(); - initInstance(instance); - runTest(instance); - message.status = "ok"; - } catch (e) { - message.status = "exception"; - if (e.$javaException && e.$javaException.constructor.$meta) { - message.exception = e.$javaException.constructor.$meta.name; - message.stack = e.$javaException.constructor.$meta.name + ": "; - var exceptionMessage = extractException(e.$javaException); - message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : ""; - message.stack += "\n" + e.stack; - } else { - message.stack = e.stack; - } - } - window.parent.postMessage(JSON.stringify(message), "*"); - }); -} -JUnitClient.reportError = function(error) { - var handler = window.addEventListener("message", function() { - window.removeEventListener("message", handler); - var message = { status : "exception", stack : error }; - window.parent.postMessage(JSON.stringify(message), "*"); - }); -} \ No newline at end of file diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit.css b/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit.css deleted file mode 100644 index d66b8c9fb..000000000 --- a/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit.css +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2013 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -table { - border-collapse: collapse; - border: 2px solid black; - margin: 2em 1em 2em 1em; -} -table td, table th { - border: 1px solid gray; - padding: 0.1em 0.5em 0.2em 0.5em; -} -table thead, table tfoot { - border: 2px solid black; -} -iframe { - with: 1px; - height: 1px; - padding: 0px; - margin: 0px; - border-style: none; -} \ No newline at end of file diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit.html b/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit.html index 45e3e79e3..06b655d11 100644 --- a/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit.html +++ b/teavm-maven-plugin/src/main/resources/org/teavm/maven/junit.html @@ -19,17 +19,35 @@ TeaVM JUnit tests TeaVM JUnit tests - - + + - +
+
Total tests: + +
+
Failed:
+
+
+
+ +
+
+
+
+
+ \ No newline at end of file diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/class_obj.png b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/class_obj.png new file mode 100644 index 000000000..c57c2d00f Binary files /dev/null and b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/class_obj.png differ diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/control-000-small.png b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/control-000-small.png new file mode 100644 index 000000000..40a7bf9d8 Binary files /dev/null and b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/control-000-small.png differ diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/junit-support.js b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/junit-support.js new file mode 100644 index 000000000..acc9746d2 --- /dev/null +++ b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/junit-support.js @@ -0,0 +1,400 @@ +/* + * Copyright 2013 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +JUnitServer = function() { + this.tree = new Tree(document.getElementById("test-tree")); + this.totalTimeSpent = 0; + this.expectedExceptions = []; + this.frame = null; + this.tests = []; + this.currentTestNode = null; + this.testCaseCount = 0; + this.progressElem = document.getElementById("progress-bar-content"); + this.runCount = 0; + this.failCount = 0; + this.traceElem = document.getElementById("test-trace"); + this.failedElem = document.getElementById("failed-test-count"); + this.totalTimeElem = document.getElementById("total-time"); + var self = this; + this.tree.addSelectionListener(function(node) { + while (self.traceElem.firstChild) { + self.traceElem.removeChild(self.traceElem.firstChild); + } + if (node.error) { + var pre = document.createElement("pre"); + pre.appendChild(document.createTextNode(node.error)); + self.traceElem.appendChild(pre); + } + }); +} +JUnitServer.prototype = new Object(); +JUnitServer.prototype.handleEvent = function(message, callback) { + if (message.status === "ok") { + if (this.expectedExceptions.length > 0) { + this.currentTestNode.success = false; + this.currentTestNode.error = "Expected exception not thrown"; + this.failCount++; + } else { + this.currentTestNode.success = true; + } + } else if (message.status === "exception") { + if (message.exception && this.isExpectedException(message.exception)) { + this.currentTestNode.success = true; + } else { + this.currentTestNode.success = false; + this.currentTestNode.error = message.stack; + this.failCount++; + } + } + this.currentTestNode.indicator.className = "complete-indicator " + + (this.currentTestNode.success ? "successfull" : "failed"); + this.runCount++; + this.progressElem.style.width = (100 * this.runCount / this.testCaseCount).toFixed(2) + "%"; + document.body.removeChild(this.frame); + self.frame = null; + callback(); +} +JUnitServer.prototype.isExpectedException = function(ex) { + for (var i = 0; i < this.expectedExceptions.length; ++i) { + if (this.expectedExceptions[i] === ex) { + return true; + } + } + return false; +} +JUnitServer.prototype.loadCode = function(path, additionalScripts, callback) { + this.frame = document.createElement("iframe"); + document.body.appendChild(this.frame); + var frameDoc = this.frame.contentWindow.document; + var self = this; + var sequence = ["res/junit-support.js", "res/runtime.js"]; + if (additionalScripts) { + for (var i = 0; i < additionalScripts.length; ++i) { + sequence.push(additionalScripts[i]); + } + } + sequence.push(path); + self.loadScripts(sequence, 0, callback); +} +JUnitServer.prototype.loadScripts = function(scripts, index, callback) { + var self = this; + if (index == scripts.length) { + callback(); + } else { + this.loadScript(scripts[index], function() { self.loadScripts(scripts, index + 1, callback) }); + } +} +JUnitServer.prototype.loadScript = function(name, callback) { + var doc = this.frame.contentWindow.document; + var script = doc.createElement("script"); + script.src = name; + doc.body.appendChild(script); + script.onload = function() { + callback(); + } +} +JUnitServer.prototype.runTest = function(node, callback) { + node.indicator.className = "complete-indicator in-progress"; + var startTime = new Date().getTime(); + if (node.testCase) { + this.expectedExceptions = node.testCase.expected; + this.currentTestNode = node; + var self = this; + this.loadCode(node.testCase.script, node.testCase.additionalScripts, function() { + messageHandler = function(event) { + window.removeEventListener("message", messageHandler); + var timeSpent = new Date().getTime() - startTime; + node.timeIndicator.appendChild(document.createTextNode( + "(" + (timeSpent / 1000).toFixed(3) + ")")); + self.handleEvent(JSON.parse(event.data), callback); + }; + window.addEventListener("message", messageHandler); + self.frame.contentWindow.postMessage("runTest", "*"); + }); + } else { + var self = this; + var nodes = node.getNodes(); + this.runTestFromList(nodes, 0, function() { + node.success = true; + for (var i = 0; i < nodes.length; ++i) { + if (!nodes[i].success) { + node.success = false; + break; + } + } + node.indicator.className = "complete-indicator " + + (node.success ? "successfull" : "failed"); + if (!node.success) { + node.open(); + } + var timeSpent = new Date().getTime() - startTime; + node.timeIndicator.appendChild(document.createTextNode( + "(" + (timeSpent / 1000).toFixed(3) + ")")); + callback(); + }); + } +} +JUnitServer.prototype.readTests = function(tests) { + var groups = this.groupTests(tests); + var groupNames = []; + for (var groupName in groups) { + groupNames.push(groupName); + } + groupNames.sort(); + this.tests = []; + for (var i = 0; i < groupNames.length; ++i) { + var groupName = groupNames[i]; + var group = groups[groupName]; + var pkgNode = this.createNode(this.tree, "package", groupName); + group.sort(); + for (var j = 0; j < group.length; ++j) { + var test = group[j]; + var simpleName = test.name.substring(groupName.length + 1); + var testNode = this.createNode(pkgNode, "test", simpleName); + var methods = test.methods.slice(); + methods.sort(); + for (var k = 0; k < methods.length; ++k) { + var method = methods[k]; + var caseNode = this.createNode(testNode, "case", method.name); + caseNode.testCase = method; + ++this.testCaseCount; + } + } + } + document.getElementById("test-count").appendChild(document.createTextNode(this.testCaseCount)); + return this; +} +JUnitServer.prototype.createNode = function(parent, className, name) { + var elem = document.createElement("div"); + elem.className = className; + elem.appendChild(document.createTextNode(name)); + var node = parent.add(elem); + node.indicator = document.createElement("div"); + node.indicator.className = "complete-indicator"; + elem.appendChild(node.indicator); + node.timeIndicator = document.createElement("span"); + node.timeIndicator.className = "time-indicator"; + elem.appendChild(node.timeIndicator); + return node; +} +JUnitServer.prototype.groupTests = function(tests) { + var groups = {}; + for (var i = 0; i < tests.length; ++i) { + var test = tests[i]; + var pkg = test.name.substring(0, test.name.lastIndexOf('.')); + var group = groups[pkg]; + if (!group) { + group = []; + groups[pkg] = group; + } + group.push(test); + } + return groups; +} +JUnitServer.prototype.runAllTests = function(callback) { + this.cleanupTests(); + var self = this; + var startTime = new Date().getTime(); + this.runTestFromList(this.tree.getNodes(), 0, function() { + self.failedElem.appendChild(document.createTextNode(self.failCount)); + var totalTime = new Date().getTime() - startTime; + self.totalTimeElem.appendChild(document.createTextNode("(" + + (totalTime / 1000).toFixed(3) + ")")); + callback(); + }); +} +JUnitServer.prototype.runTestFromList = function(nodes, index, callback) { + if (index < nodes.length) { + var node = nodes[index]; + var self = this; + this.runTest(node, function() { + self.runTestFromList(nodes, index + 1, callback); + }); + } else { + callback(); + } +} +JUnitServer.prototype.cleanupTests = function() { + if (this.failedElem.firstChild) { + this.failedElem.removeChild(this.failedElem.firstChild); + } + if (this.totalTimeElem.firstChild) { + this.totalTimeElem.removeChild(this.totalTimeElem.firstChild); + } + this.runCount = 0; + this.progressElem.style.width = "0%"; + var nodes = this.tree.getNodes(); + for (var i = 0; i < nodes.length; ++i) { + this.cleanupNode(nodes[i]); + } +} +JUnitServer.prototype.cleanupNode = function(node) { + delete node.error; + node.indicator.className = "complete-indicator"; + if (node.timeIndicator.firstChild) { + node.timeIndicator.removeChild(node.timeIndicator.firstChild); + } + var nodes = node.getNodes(); + for (var i = 0; i < nodes.length; ++i) { + this.cleanupNode(nodes[i]); + } +} + +Tree = function(container) { + this.container = container; + this.nodes = []; + this.selectedNode = null; + this.selectionListeners = []; +} +Tree.prototype.createNode = function(content) { + var elem = document.createElement("div"); + elem.className = "tree-node"; + var contentElem = document.createElement("div"); + contentElem.className = "tree-node-content"; + elem.appendChild(contentElem); + contentElem.appendChild(content); + var buttonElem = document.createElement("div"); + buttonElem.className = "tree-node-button closed"; + buttonElem.style.display = "none"; + elem.appendChild(buttonElem); + var childrenElem = document.createElement("div"); + childrenElem.className = "tree-node-children closed"; + childrenElem.style.display = "none"; + elem.appendChild(childrenElem); + return new TreeNode(elem, contentElem, buttonElem, childrenElem, this); +} +Tree.prototype.add = function(content) { + var node = this.createNode(content); + this.container.appendChild(node.elem); + this.nodes.push(node); + return node; +} +Tree.prototype.getNodes = function() { + return this.nodes; +} +Tree.prototype.addSelectionListener = function(listener) { + this.selectionListeners.push(listener); +} +TreeNode = function(elem, content, button, children, tree) { + this.elem = elem; + this.content = content; + this.button = button; + this.children = children; + this.opened = false; + this.parent = null; + this.nodes = []; + this.tree = tree; + var self = this; + this.button.onclick = function() { + self.toggle(); + } + this.content.onclick = function() { + self.select(); + } +} +TreeNode.prototype.add = function(content) { + var node = this.tree.createNode(content); + this.children.appendChild(node.elem); + this.button.style.display = ""; + this.children.style.display = ""; + this.content.className = "tree-node-content"; + node.parent = this; + this.nodes.push(node); + return node; +} +TreeNode.prototype.isOpened = function() { + return this.opened; +} +TreeNode.prototype.open = function() { + if (this.isOpened()) { + return; + } + this.opened = true; + this.children.className = "tree-node-children opened"; + this.button.className = "tree-node-button opened"; +} +TreeNode.prototype.close = function() { + if (!this.isOpened()) { + return; + } + this.opened = false; + this.children.className = "tree-node-children closed"; + this.button.className = "tree-node-button closed"; +} +TreeNode.prototype.toggle = function() { + if (this.isOpened()) { + this.close(); + } else { + this.open(); + } +} +TreeNode.prototype.getNodes = function() { + return this.nodes; +} +TreeNode.prototype.getParent = function() { + return this.parent; +} +TreeNode.prototype.getTree = function() { + return this.tree; +} +TreeNode.prototype.isSelected = function() { + return this.tree.selectedNode === this; +} +TreeNode.prototype.select = function() { + if (this.isSelected()) { + return; + } + if (this.tree.selectedNode != null) { + this.tree.selectedNode.content.className = "tree-node-content"; + } + this.content.className = "tree-node-content selected"; + this.tree.selectedNode = this; + for (var i = 0; i < this.tree.selectionListeners.length; ++i) { + this.tree.selectionListeners[i](this); + } +} + +JUnitClient = {}; +JUnitClient.run = function() { + var handler = window.addEventListener("message", function() { + window.removeEventListener("message", handler); + var message = {}; + try { + var instance = new TestClass(); + initInstance(instance); + runTest(instance); + message.status = "ok"; + } catch (e) { + message.status = "exception"; + if (e.$javaException && e.$javaException.constructor.$meta) { + message.exception = e.$javaException.constructor.$meta.name; + message.stack = e.$javaException.constructor.$meta.name + ": "; + var exceptionMessage = extractException(e.$javaException); + message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : ""; + message.stack += "\n" + e.stack; + } else { + message.stack = e.stack; + } + } + window.parent.postMessage(JSON.stringify(message), "*"); + }); +} +JUnitClient.reportError = function(error) { + var handler = window.addEventListener("message", function() { + window.removeEventListener("message", handler); + var message = { status : "exception", stack : error }; + window.parent.postMessage(JSON.stringify(message), "*"); + }); +} \ No newline at end of file diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/junit.css b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/junit.css new file mode 100644 index 000000000..b239c4695 --- /dev/null +++ b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/junit.css @@ -0,0 +1,185 @@ +/* + * Copyright 2013 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +iframe { + with: 1px; + height: 1px; + padding: 0px; + margin: 0px; + border-style: none; +} +* { + font-family: sans; + font-size: 10pt; +} +html, body { + position: absolute; + left: 0px; + right: 0px; + top: 0px; + bottom: 0px; + padding: 0px; + margin: 0px; +} +.test-header { + position: absolute; + top: 0px; + right: 0px; + left: 0px; + height: 40px; + border-bottom-color: gray; + border-bottom-width: 1px; + border-bottom-style: solid; +} +.test-count, .failed-test-count { + position: absolute; + top: 0px; + bottom: 0px; + line-height: 40px; + padding: 0px; + margin: 0px; + vertical-align: middle; +} +.test-count { + left: 5px; + width: 200px; +} +.failed-test-count { + left: 205px; + width: 130px; +} +#start-button { + display: block; + position: absolute; + left: 340px; + top: 6px; + bottom: 6px; + width: 70px; +} +.progress-bar { + position: absolute; + left: 420px; + right: 20px; + top: 5px; + height: 30px; + border-color: grey; + border-radius: 2px; + border-width: 1px; + border-style: solid; +} +#progress-bar-content { + position: absolute; + background-color: green; + left: 0px; + top: 0px; + bottom: 0px; + width: 0%; +} +#test-tree { + position: absolute; + top: 41px; + bottom: 0px; + left: 0px; + width: 50%; + border-right-color: grey; + border-right-width: 1px; + border-right-style: solid; + overflow: auto; +} +#test-trace { + position: absolute; + top: 41px; + bottom: 0px; + left: 50%; + right: 0px; + overflow: auto; +} +.tree-node { + position: relative; +} +.tree-node-content { + margin-left: 18px; + cursor: default; + white-space: nowrap; +} +.tree-node-content.selected { + background-color: rgb(160,160,255); +} +.tree-node-children { + padding-left: 18px; +} +.tree-node-children.closed { + display: none; +} +.tree-node-button { + position: absolute; + cursor: pointer; + left: 0px; + top: 0px; + width: 16px; + height: 16px; + background-repeat: no-repeat; + background-position: 0px 0px; +} +.tree-node-button.opened { + background-image: url(toggle-small.png); +} +.tree-node-button.closed { + background-image: url(toggle-small-expand.png); +} +.package, .test, .case { + min-height: 18px; + padding-left: 20px; + background-position: 2px 1px; + background-repeat: no-repeat; + position: relative; +} +.package { + background-image: url(package_obj.png); +} +.test { + background-image: url(class_obj.png); +} +.case { + background-image: url(methpub_obj.png); +} +.complete-indicator { + position: absolute; + left: -4px; + top: 5px; + width: 16px; + height: 16px; + background-position: 0px 0px; + background-repeat: no-repeat; + display: none; +} +.complete-indicator.successfull { + display: block; + background-image: url(tick-small.png); +} +.complete-indicator.failed { + display: block; + background-image: url(tick-small-red.png); +} +.complete-indicator.in-progress { + display: block; + background-image: url(control-000-small.png); +} +.time-indicator { + font-size: 9pt; + font-style: italic; + color: rgb(0,80,160); + padding-left: 0.5em; +} \ No newline at end of file diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/methpub_obj.png b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/methpub_obj.png new file mode 100644 index 000000000..300419528 Binary files /dev/null and b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/methpub_obj.png differ diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/package_obj.png b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/package_obj.png new file mode 100644 index 000000000..0afea5b86 Binary files /dev/null and b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/package_obj.png differ diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/runtime.js b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/runtime.js new file mode 100644 index 000000000..4ab5dea35 --- /dev/null +++ b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/runtime.js @@ -0,0 +1,732 @@ +/* + * Copyright 2013 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +$rt_lastObjectId = 0; +$rt_nextId = function() { + return $rt_lastObjectId++; +} +$rt_compare = function(a, b) { + return a > b ? 1 : a < b ? -1 : 0; +} +$rt_isInstance = function(obj, cls) { + return obj != null && obj.constructor.$meta && $rt_isAssignable(obj.constructor, cls); +} +$rt_isAssignable = function(from, to) { + if (from === to) { + return true; + } + var supertypes = from.$meta.supertypes; + for (var i = 0; i < supertypes.length; i = (i + 1) | 0) { + if ($rt_isAssignable(supertypes[i], to)) { + return true; + } + } + return false; +} +$rt_createArray = function(cls, sz) { + var data = new Array(sz); + var arr = new ($rt_arraycls(cls))(data); + for (var i = 0; i < sz; i = (i + 1) | 0) { + data[i] = null; + } + return arr; +} +$rt_wrapArray = function(cls, data) { + var arr = new ($rt_arraycls(cls))(data); + return arr; +} +$rt_createUnfilledArray = function(cls, sz) { + return new ($rt_arraycls(cls))(new Array(sz)); +} +$rt_createLongArray = function(sz) { + var data = new Array(sz); + var arr = new ($rt_arraycls($rt_longcls()))(data); + for (var i = 0; i < sz; i = (i + 1) | 0) { + data[i] = Long.ZERO; + } + return arr; +} +if (ArrayBuffer) { + $rt_createNumericArray = function(cls, nativeArray) { + return new ($rt_arraycls(cls))(nativeArray); + } + $rt_createByteArray = function(sz) { + return $rt_createNumericArray($rt_bytecls(), new Int8Array(new ArrayBuffer(sz)), 0); + }; + $rt_createShortArray = function(sz) { + return $rt_createNumericArray($rt_shortcls(), new Int16Array(new ArrayBuffer(sz << 1)), 0); + }; + $rt_createIntArray = function(sz) { + return $rt_createNumericArray($rt_intcls(), new Int32Array(new ArrayBuffer(sz << 2)), 0); + }; + $rt_createBooleanArray = function(sz) { + return $rt_createNumericArray($rt_booleancls(), new Int8Array(new ArrayBuffer(sz)), 0); + }; + $rt_createFloatArray = function(sz) { + return $rt_createNumericArray($rt_floatcls(), new Float32Array(new ArrayBuffer(sz << 2)), 0); + }; + $rt_createDoubleArray = function(sz) { + return $rt_createNumericArray($rt_doublecls(), new Float64Array(new ArrayBuffer(sz << 3)), 0); + }; + $rt_createCharArray = function(sz) { + return $rt_createNumericArray($rt_charcls(), new Uint16Array(new ArrayBuffer(sz << 1)), 0); + }; +} else { + $rt_createNumericArray = function(cls, sz) { + var data = new Array(sz); + var arr = new ($rt_arraycls(cls))(data); + for (var i = 0; i < sz; i = (i + 1) | 0) { + data[i] = 0; + } + return arr; + } + $rt_createByteArray = function(sz) { return $rt_createNumericArray($rt_bytecls(), sz); } + $rt_createShortArray = function(sz) { return $rt_createNumericArray($rt_shortcls(), sz); } + $rt_createIntArray = function(sz) { return $rt_createNumericArray($rt_intcls(), sz); } + $rt_createBooleanArray = function(sz) { return $rt_createNumericArray($rt_booleancls(), sz); } + $rt_createFloatArray = function(sz) { return $rt_createNumericArray($rt_floatcls(), sz); } + $rt_createDoubleArray = function(sz) { return $rt_createNumericArray($rt_doublecls(), sz); } + $rt_createCharArray = function(sz) { return $rt_createNumericArray($rt_charcls(), sz); } +} +$rt_arraycls = function(cls) { + if (cls.$array == undefined) { + var arraycls = function(data) { + this.data = data; + this.$id = $rt_nextId(); + }; + arraycls.prototype = new ($rt_objcls())(); + arraycls.prototype.constructor = arraycls; + arraycls.$meta = { item : cls, supertypes : [$rt_objcls()], primitive : false, superclass : $rt_objcls() }; + cls.$array = arraycls; + } + return cls.$array; +} +$rt_createcls = function() { + return { + $meta : { + supertypes : [] + } + }; +} +$rt_booleanclsCache = null; +$rt_booleancls = function() { + if ($rt_booleanclsCache == null) { + $rt_booleanclsCache = $rt_createcls(); + $rt_booleanclsCache.primitive = true; + $rt_booleanclsCache.name = "boolean"; + } + return $rt_booleanclsCache; +} +$rt_charclsCache = null; +$rt_charcls = function() { + if ($rt_charclsCache == null) { + $rt_charclsCache = $rt_createcls(); + $rt_charclsCache.primitive = true; + $rt_charclsCache.name = "char"; + } + return $rt_charclsCache; +} +$rt_byteclsCache = null; +$rt_bytecls = function() { + if ($rt_byteclsCache == null) { + $rt_byteclsCache = $rt_createcls(); + $rt_byteclsCache.primitive = true; + $rt_byteclsCache.name = "byte"; + } + return $rt_byteclsCache; +} +$rt_shortclsCache = null; +$rt_shortcls = function() { + if ($rt_shortclsCache == null) { + $rt_shortclsCache = $rt_createcls(); + $rt_shortclsCache.primitive = true; + $rt_shortclsCache.name = "short"; + } + return $rt_shortclsCache; +} +$rt_intclsCache = null; +$rt_intcls = function() { + if ($rt_intclsCache === null) { + $rt_intclsCache = $rt_createcls(); + $rt_intclsCache.primitive = true; + $rt_intclsCache.name = "int"; + } + return $rt_intclsCache; +} +$rt_longclsCache = null; +$rt_longcls = function() { + if ($rt_longclsCache === null) { + $rt_longclsCache = $rt_createcls(); + $rt_longclsCache.primitive = true; + $rt_longclsCache.name = "long"; + } + return $rt_longclsCache; +} +$rt_floatclsCache = null; +$rt_floatcls = function() { + if ($rt_floatclsCache === null) { + $rt_floatclsCache = $rt_createcls(); + $rt_floatclsCache.primitive = true; + $rt_floatclsCache.name = "float"; + } + return $rt_floatclsCache; +} +$rt_doubleclsCache = null; +$rt_doublecls = function() { + if ($rt_doubleclsCache === null) { + $rt_doubleclsCache = $rt_createcls(); + $rt_doubleclsCache.primitive = true; + $rt_doubleclsCache.name = "double"; + } + return $rt_doubleclsCache; +} +$rt_voidclsCache = null; +$rt_voidcls = function() { + if ($rt_voidclsCache === null) { + $rt_voidclsCache = $rt_createcls(); + $rt_voidclsCache.primitive = true; + $rt_voidclsCache.name = "void"; + } + return $rt_voidclsCache; +} +$rt_equals = function(a, b) { + if (a === b) { + return true; + } + if (a === null || b === null) { + return false; + } + if (typeof(a) == 'object') { + return a.equals(b); + } else { + return false; + } +} +$rt_clinit = function(cls) { + if (cls.$clinit) { + var f = cls.$clinit; + delete cls.$clinit; + f(); + } + return cls; +} +$rt_init = function(cls, constructor, args) { + var obj = new cls(); + cls.prototype[constructor].apply(obj, args); + return obj; +} +$rt_throw = function(ex) { + var err = ex.$jsException; + if (!err) { + var err = new Error("Java exception thrown"); + err.$javaException = ex; + ex.$jsException = err; + } + throw err; +} +$rt_byteToInt = function(value) { + return value > 0xFF ? value | 0xFFFFFF00 : value; +} +$rt_shortToInt = function(value) { + return value > 0xFFFF ? value | 0xFFFF0000 : value; +} +$rt_createMultiArray = function(cls, dimensions) { + var arrays = new Array($rt_primitiveArrayCount(dimensions)); + var firstDim = dimensions[0] | 0; + for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { + arrays[i] = $rt_createArray(cls, firstDim); + } + return $rt_createMultiArrayImpl(cls, arrays, dimensions); +} +$rt_createByteMultiArray = function(dimensions) { + var arrays = new Array($rt_primitiveArrayCount(dimensions)); + var firstDim = dimensions[0] | 0; + for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { + arrays[i] = $rt_createByteArray(firstDim); + } + return $rt_createMultiArrayImpl($rt_bytecls(), arrays, dimensions); +} +$rt_createBooleanMultiArray = function(dimensions) { + var arrays = new Array($rt_primitiveArrayCount(dimensions)); + var firstDim = dimensions[0] | 0; + for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { + arrays[i] = $rt_createBooleanArray(firstDim); + } + return $rt_createMultiArrayImpl($rt_booleancls(), arrays, dimensions); +} +$rt_createShortMultiArray = function(dimensions) { + var arrays = new Array($rt_primitiveArrayCount(dimensions)); + var firstDim = dimensions[0] | 0; + for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { + arrays[i] = $rt_createShortArray(firstDim); + } + return $rt_createMultiArrayImpl($rt_shortcls(), arrays, dimensions); +} +$rt_createIntMultiArray = function(dimensions) { + var arrays = new Array($rt_primitiveArrayCount(dimensions)); + var firstDim = dimensions[0] | 0; + for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { + arrays[i] = $rt_createIntArray(firstDim); + } + return $rt_createMultiArrayImpl($rt_intcls(), arrays, dimensions); +} +$rt_createLongMultiArray = function(dimensions) { + var arrays = new Array($rt_primitiveArrayCount(dimensions)); + var firstDim = dimensions[0] | 0; + for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { + arrays[i] = $rt_createLongArray(firstDim); + } + return $rt_createMultiArrayImpl($rt_longcls(), arrays, dimensions); +} +$rt_createFloatMultiArray = function(dimensions) { + var arrays = new Array($rt_primitiveArrayCount(dimensions)); + var firstDim = dimensions[0] | 0; + for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { + arrays[i] = $rt_createFloatArray(firstDim); + } + return $rt_createMultiArrayImpl($rt_floatcls(), arrays, dimensions); +} +$rt_createDoubleMultiArray = function(dimensions) { + var arrays = new Array($rt_primitiveArrayCount(dimensions)); + var firstDim = dimensions[0] | 0; + for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) { + arrays[i] = $rt_createDoubleArray(firstDim); + } + return $rt_createMultiArrayImpl($rt_doublecls(), arrays, dimensions); +} +$rt_primitiveArrayCount = function(dimensions) { + var val = dimensions[1] | 0; + for (var i = 2 | 0; i < dimensions.length; i = (i + 1) | 0) { + val = (val * (dimensions[i] | 0)) | 0; + } + return val; +} +$rt_createMultiArrayImpl = function(cls, arrays, dimensions) { + var limit = arrays.length; + for (var i = 1 | 0; i < dimensions.length; i = (i + 1) | 0) { + cls = $rt_arraycls(cls); + var dim = dimensions[i]; + var index = 0; + var packedIndex = 0; + while (index < limit) { + var arr = $rt_createUnfilledArray(cls, dim); + for (var j = 0; j < dim; j = (j + 1) | 0) { + arr.data[j] = arrays[index]; + index = (index + 1) | 0; + } + arrays[packedIndex] = arr; + packedIndex = (packedIndex + 1) | 0; + } + limit = packedIndex; + } + return arrays[0]; +} +$rt_assertNotNaN = function(value) { + if (typeof value == 'number' && isNaN(value)) { + throw "NaN"; + } + return value; +} +$rt_methodStubs = function(clinit, names) { + for (var i = 0; i < names.length; i = (i + 1) | 0) { + window[names[i]] = (function(name) { + return function() { + clinit(); + return window[name].apply(window, arguments); + } + })(names[i]); + } +} +$rt_stdoutBuffer = ""; +$rt_putStdout = function(ch) { + if (ch === 0xA) { + if (console) { + console.info($rt_stdoutBuffer); + } + $rt_stdoutBuffer = ""; + } else { + $rt_stdoutBuffer += String.fromCharCode(ch); + } +} +$rt_stderrBuffer = ""; +$rt_putStderr = function(ch) { + if (ch === 0xA) { + if (console) { + console.info($rt_stderrBuffer); + } + $rt_stderrBuffer = ""; + } else { + $rt_stderrBuffer += String.fromCharCode(ch); + } +} +function $rt_declClass(cls, data) { + cls.name = data.name; + cls.$meta = {}; + cls.$meta.superclass = data.superclass; + cls.$meta.supertypes = data.interfaces ? data.interfaces.slice() : []; + if (data.superclass) { + cls.$meta.supertypes.push(data.superclass); + cls.prototype = new data.superclass(); + } else { + cls.prototype = new Object(); + } + cls.$meta.name = data.name; + cls.$meta.enum = data.enum; + cls.prototype.constructor = cls; + cls.$clinit = data.clinit; +} +function $rt_virtualMethods(cls) { + for (var i = 1; i < arguments.length; i += 2) { + var name = arguments[i]; + var func = arguments[i + 1]; + if (typeof name == 'string') { + cls.prototype[name] = func; + } else { + for (var j = 0; j < name.length; ++j) { + cls.prototype[name[j]] = func; + } + } + } +} + +Long = function(lo, hi) { + this.lo = lo | 0; + this.hi = hi | 0; +} +Long_ZERO = new Long(0, 0); +Long_fromInt = function(val) { + return val >= 0 ? new Long(val, 0) : new Long(val, -1); +} +Long_fromNumber = function(val) { + return new Long(val | 0, (val / 0x100000000) | 0); +} +Long_toNumber = function(val) { + return val.hi >= 0 ? val.lo + 0x100000000 * val.hi : -0x100000000 * (val.hi ^ 0xFFFFFFFF) + val.lo; +} +Long_add = function(a, b) { + var a_lolo = a.lo & 0xFFFF; + var a_lohi = a.lo >>> 16; + var a_hilo = a.hi & 0xFFFF; + var a_hihi = a.hi >>> 16; + var b_lolo = b.lo & 0xFFFF; + var b_lohi = b.lo >>> 16; + var b_hilo = b.hi & 0xFFFF; + var b_hihi = b.hi >>> 16; + + var lolo = (a_lolo + b_lolo) | 0; + var lohi = (a_lohi + b_lohi + (lolo >> 16)) | 0; + var hilo = (a_hilo + b_hilo + (lohi >> 16)) | 0; + var hihi = (a_hihi + b_hihi + (hilo >> 16)) | 0; + return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), + (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16)); +} +Long_inc = function(a) { + var lo = (a.lo + 1) | 0; + var hi = a.hi; + if (lo === 0) { + hi = (hi + 1) | 0; + } + return new Long(lo, hi); +} +Long_dec = function(a) { + var lo = (a.lo - 1) | 0; + var hi = a.hi; + if (lo === -1) { + hi = (hi - 1) | 0; + } + return new Long(lo, hi); +} +Long_neg = function(a) { + return Long_inc(new Long(a.lo ^ 0xFFFFFFFF, a.hi ^ 0xFFFFFFFF)); +} +Long_sub = function(a, b) { + var a_lolo = a.lo & 0xFFFF; + var a_lohi = a.lo >>> 16; + var a_hilo = a.hi & 0xFFFF; + var a_hihi = a.hi >>> 16; + var b_lolo = b.lo & 0xFFFF; + var b_lohi = b.lo >>> 16; + var b_hilo = b.hi & 0xFFFF; + var b_hihi = b.hi >>> 16; + + var lolo = (a_lolo - b_lolo) | 0; + var lohi = (a_lohi - b_lohi + (lolo >> 16)) | 0; + var hilo = (a_hilo - b_hilo + (lohi >> 16)) | 0; + var hihi = (a_hihi - b_hihi + (hilo >> 16)) | 0; + return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), + (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16)); +} +Long_compare = function(a, b) { + var r = a.hi - b.hi; + if (r !== 0) { + return r; + } + var r = (a.lo >>> 1) - (b.lo >>> 1); + if (r !== 0) { + return r; + } + return (a.lo & 1) - (b.lo & 1); +} +Long_isPositive = function(a) { + return (a.hi & 0x80000000) === 0; +} +Long_isNegative = function(a) { + return (a.hi & 0x80000000) !== 0; +} +Long_mul = function(a, b) { + var positive = Long_isNegative(a) === Long_isNegative(b); + if (Long_isNegative(a)) { + a = Long_neg(a); + } + if (Long_isNegative(b)) { + b = Long_neg(b); + } + var a_lolo = a.lo & 0xFFFF; + var a_lohi = a.lo >>> 16; + var a_hilo = a.hi & 0xFFFF; + var a_hihi = a.hi >>> 16; + var b_lolo = b.lo & 0xFFFF; + var b_lohi = b.lo >>> 16; + var b_hilo = b.hi & 0xFFFF; + var b_hihi = b.hi >>> 16; + + var lolo = (a_lolo * b_lolo) | 0; + var lohi = (a_lohi * b_lolo + a_lolo * b_lohi + (lolo >> 16)) | 0; + var hilo = (a_hilo * b_lolo + a_lohi * b_lohi + a_lolo * b_hilo + (lohi >> 16)) | 0; + var hihi = (a_hihi * b_lolo + a_hilo * b_lohi + a_lohi * b_hilo + a_lolo * b_hihi + (hilo >> 16)) | 0; + var result = new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16)); + return positive ? result : Long_neg(result); +} +Long_div = function(a, b) { + return Long_divRem(a, b)[0]; +} +Long_rem = function(a, b) { + return Long_divRem(a, b)[1]; +} +Long_divRem = function(a, b) { + var positive = Long_isNegative(a) === Long_isNegative(b); + if (Long_isNegative(a)) { + a = Long_neg(a); + } + if (Long_isNegative(b)) { + b = Long_neg(b); + } + a = new LongInt(a.lo, a.hi, 0); + b = new LongInt(b.lo, b.hi, 0); + var q = LongInt_div(a, b); + a = new Long(a.lo, a.hi); + q = new Long(q.lo, q.hi); + return positive ? [q, a] : [Long_neg(q), Long_neg(a)]; +} +Long_shiftLeft16 = function(a) { + return new Long(a.lo << 16, (a.lo >>> 16) | (a.hi << 16)); +} +Long_shiftRight16 = function(a) { + return new Long((a.lo >>> 16) | (a.hi << 16), a.hi >>> 16); +} +Long_and = function(a, b) { + return new Long(a.lo & b.lo, a.hi & b.hi); +} +Long_or = function(a, b) { + return new Long(a.lo | b.lo, a.hi | b.hi); +} +Long_xor = function(a, b) { + return new Long(a.lo ^ b.lo, a.hi ^ b.hi); +} +Long_shl = function(a, b) { + if (b < 32) { + return new Long(a.lo << b, (a.lo >>> (32 - b)) | (a.hi << b)); + } else { + return new Long(0, a.lo << (b - 32)); + } +} +Long_shr = function(a, b) { + if (b < 32) { + return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >> b); + } else { + return new Long((a.hi >> (b - 32)), -1); + } +} +Long_shru = function(a, b) { + if (b < 32) { + return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >>> b); + } else { + return new Long((a.hi >>> (b - 32)), 0); + } +} + +// Represents a mutable 80-bit unsigned integer +LongInt = function(lo, hi, sup) { + this.lo = lo; + this.hi = hi; + this.sup = sup; +} +LongInt_mul = function(a, b) { + var a_lolo = ((a.lo & 0xFFFF) * b) | 0; + var a_lohi = ((a.lo >>> 16) * b) | 0; + var a_hilo = ((a.hi & 0xFFFF) * b) | 0; + var a_hihi = ((a.hi >>> 16) * b) | 0; + var sup = (a.sup * b) | 0; + + a_lohi = (a_lohi + (a_lolo >> 16)) | 0; + a_hilo = (a_hilo + (a_lohi >> 16)) | 0; + a_hihi = (a_hihi + (a_hilo >> 16)) | 0; + sup = (sup + (a_hihi >> 16)) | 0; + a.lo = (a_lolo & 0xFFFF) | (a_lohi << 16); + a.hi = (a_hilo & 0xFFFF) | (a_hihi << 16); + a.sup = sup & 0xFFFF; +} +LongInt_sub = function(a, b) { + var a_lolo = a.lo & 0xFFFF; + var a_lohi = a.lo >>> 16; + var a_hilo = a.hi & 0xFFFF; + var a_hihi = a.hi >>> 16; + var b_lolo = b.lo & 0xFFFF; + var b_lohi = b.lo >>> 16; + var b_hilo = b.hi & 0xFFFF; + var b_hihi = b.hi >>> 16; + + a_lolo = (a_lolo - b_lolo) | 0; + a_lohi = (a_lohi - b_lohi + (a_lolo >> 16)) | 0; + a_hilo = (a_hilo - b_hilo + (a_lohi >> 16)) | 0; + a_hihi = (a_hihi - b_hihi + (a_hilo >> 16)) | 0; + sup = (a.sup - b.sup + (a_hihi >> 16)) | 0; + a.lo = (a_lolo & 0xFFFF) | ((a_lohi & 0xFFFF) << 16); + a.hi = (a_hilo & 0xFFFF) | ((a_hihi & 0xFFFF) << 16); + a.sup = sup; +} +LongInt_add = function(a, b) { + var a_lolo = a.lo & 0xFFFF; + var a_lohi = a.lo >>> 16; + var a_hilo = a.hi & 0xFFFF; + var a_hihi = a.hi >>> 16; + var b_lolo = b.lo & 0xFFFF; + var b_lohi = b.lo >>> 16; + var b_hilo = b.hi & 0xFFFF; + var b_hihi = b.hi >>> 16; + + a_lolo = (a_lolo + b_lolo) | 0; + a_lohi = (a_lohi + b_lohi + (a_lolo >> 16)) | 0; + a_hilo = (a_hilo + b_hilo + (a_lohi >> 16)) | 0; + a_hihi = (a_hihi + b_hihi + (a_hilo >> 16)) | 0; + sup = (a.sup + b.sup + (a_hihi >> 16)) | 0; + a.lo = (a_lolo & 0xFFFF) | (a_lohi << 16); + a.hi = (a_hilo & 0xFFFF) | (a_hihi << 16); + a.sup = sup; +} +LongInt_ucompare = function(a, b) { + var r = (a.sup - b.sup); + if (r != 0) { + return r; + } + var r = (a.hi >>> 1) - (b.hi >>> 1); + if (r != 0) { + return r; + } + var r = (a.hi & 1) - (b.hi & 1); + if (r != 0) { + return r; + } + var r = (a.lo >>> 1) - (b.lo >>> 1); + if (r != 0) { + return r; + } + return (a.lo & 1) - (b.lo & 1); +} +LongInt_numOfLeadingZeroBits = function(a) { + var n = 0; + var d = 16; + while (d > 0) { + if ((a >>> d) !== 0) { + a >>>= d; + n = (n + d) | 0; + } + d = (d / 2) | 0; + } + return 31 - n; +} +LongInt_shl = function(a, b) { + if (b < 32) { + a.sup = ((a.hi >>> (32 - b)) | (a.sup << b)) & 0xFFFF; + a.hi = (a.lo >>> (32 - b)) | (a.hi << b); + a.lo <<= b; + } else if (b < 64) { + a.sup = ((a.lo >>> (64 - b)) | (a.hi << (b - 32))) & 0xFFFF; + a.hi = a.lo << b; + a.lo = 0; + } else { + a.sup = (a.lo << (b - 64)) & 0xFFFF; + a.hi = 0; + a.lo = 0; + } +} +LongInt_shr = function(a, b) { + if (b < 32) { + a.lo = (a.lo >>> b) | (a.hi << (32 - b)); + a.hi = (a.hi >>> b) | (a.sup << (32 - b)); + a.sup >>>= b; + } else if (b < 64) { + a.lo = (a.hi >>> (b - 32)) | (a.sup << (64 - b)); + a.hi = a.sup >>> (b - 32); + a.sup = 0; + } else { + a.lo = a.sup >>> (b - 64); + a.hi = 0; + a.sup = 0; + } +} +LongInt_copy = function(a) { + return new LongInt(a.lo, a.hi, a.sup); +} +LongInt_div = function(a, b) { + // Normalize divisor + var bits = b.hi !== 0 ? LongInt_numOfLeadingZeroBits(b.hi) : LongInt_numOfLeadingZeroBits(b.lo) + 32; + var sz = 1 + ((bits / 16) | 0); + var dividentBits = bits % 16; + LongInt_shl(b, bits); + LongInt_shl(a, dividentBits); + q = new LongInt(0, 0, 0); + while (sz-- > 0) { + LongInt_shl(q, 16); + // Calculate approximate q + var digitA = (a.hi >>> 16) + (0x10000 * a.sup); + var digitB = b.hi >>> 16; + var digit = (digitA / digitB) | 0; + var t = LongInt_copy(b); + LongInt_mul(t, digit); + // Adjust q either down or up + if (LongInt_ucompare(t, a) >= 0) { + while (LongInt_ucompare(t, a) > 0) { + LongInt_sub(t, b); + q = (q - 1) | 0; + } + } else { + while (true) { + var nextT = LongInt_copy(t); + LongInt_add(nextT, b); + if (LongInt_ucompare(nextT, a) > 0) { + break; + } + t = nextT; + q = (q + 1) | 0; + } + } + LongInt_sub(a, t); + q.lo |= digit; + LongInt_shl(a, 16); + } + LongInt_shr(a, bits + 16); + return q; +} \ No newline at end of file diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/tick-small-red.png b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/tick-small-red.png new file mode 100644 index 000000000..5b95ec59e Binary files /dev/null and b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/tick-small-red.png differ diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/tick-small.png b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/tick-small.png new file mode 100644 index 000000000..1c0ffff6d Binary files /dev/null and b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/tick-small.png differ diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/toggle-small-expand.png b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/toggle-small-expand.png new file mode 100644 index 000000000..79c5ff7e8 Binary files /dev/null and b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/toggle-small-expand.png differ diff --git a/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/toggle-small.png b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/toggle-small.png new file mode 100644 index 000000000..f783a6f2c Binary files /dev/null and b/teavm-maven-plugin/src/main/resources/org/teavm/maven/res/toggle-small.png differ