Add stack deobfuscation to dev server

This commit is contained in:
Alexey Andreev 2019-01-24 16:28:30 +03:00
parent 68522811f2
commit 78c55437e3
25 changed files with 450 additions and 6 deletions

View File

@ -10,6 +10,7 @@
</option> </option>
<option name="goals"> <option name="goals">
<list> <list>
<option value="-e" />
<option value="install" /> <option value="install" />
<option value="-DskipTests" /> <option value="-DskipTests" />
<option value="-Dteavm.build.all=false" /> <option value="-Dteavm.build.all=false" />

View File

@ -490,6 +490,7 @@ public class Renderer implements RenderingManager {
writer.append(',').softNewLine(); writer.append(',').softNewLine();
} }
first = false; first = false;
debugEmitter.emitClass(cls.getName());
writer.appendClass(cls.getName()).append(",").ws(); writer.appendClass(cls.getName()).append(",").ws();
if (classesRequiringName.contains(cls.getName())) { if (classesRequiringName.contains(cls.getName())) {
@ -541,6 +542,7 @@ public class Renderer implements RenderingManager {
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods); collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
renderVirtualDeclarations(virtualMethods); renderVirtualDeclarations(virtualMethods);
debugEmitter.emitClass(null);
} }
writer.append("]);").newLine(); writer.append("]);").newLine();
} }

View File

@ -71,6 +71,10 @@ public final class TeaVMDevServerRunner {
.withDescription("display indicator on web page") .withDescription("display indicator on web page")
.withLongOpt("indicator") .withLongOpt("indicator")
.create('i')); .create('i'));
options.addOption(OptionBuilder
.withDescription("deobfuscate stack traces")
.withLongOpt("deobfuscate-stack")
.create());
options.addOption(OptionBuilder options.addOption(OptionBuilder
.withDescription("automatically reload page when compilation completes") .withDescription("automatically reload page when compilation completes")
.withLongOpt("auto-reload") .withLongOpt("auto-reload")
@ -123,6 +127,7 @@ public final class TeaVMDevServerRunner {
parseOutputOptions(); parseOutputOptions();
devServer.setIndicator(commandLine.hasOption("indicator")); devServer.setIndicator(commandLine.hasOption("indicator"));
devServer.setDeobfuscateStack(commandLine.hasOption("deobfuscate-stack"));
devServer.setReloadedAutomatically(commandLine.hasOption("auto-reload")); devServer.setReloadedAutomatically(commandLine.hasOption("auto-reload"));
devServer.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v'))); devServer.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v')));
if (commandLine.hasOption("port")) { if (commandLine.hasOption("port")) {

View File

@ -122,6 +122,12 @@ public final class TeaVMRunner {
.hasArg() .hasArg()
.withDescription("WebAssembly binary version (currently, only 1 is supported)") .withDescription("WebAssembly binary version (currently, only 1 is supported)")
.create()); .create());
options.addOption(OptionBuilder
.withLongOpt("entry-point")
.withArgName("name")
.hasArg()
.withDescription("Entry point name in target language (main by default)")
.create("e"));
options.addOption(OptionBuilder options.addOption(OptionBuilder
.withLongOpt("min-heap") .withLongOpt("min-heap")
.withArgName("size") .withArgName("size")
@ -166,6 +172,10 @@ public final class TeaVMRunner {
parseWasmOptions(); parseWasmOptions();
parseHeap(); parseHeap();
if (commandLine.hasOption("e")) {
tool.setEntryPointName(commandLine.getOptionValue("e"));
}
interactive = commandLine.hasOption('w'); interactive = commandLine.hasOption('w');
String[] args = commandLine.getArgs(); String[] args = commandLine.getArgs();

View File

@ -70,6 +70,7 @@ public class TeaVMTool {
private String targetFileName = ""; private String targetFileName = "";
private boolean minifying = true; private boolean minifying = true;
private String mainClass; private String mainClass;
private String entryPointName = "main";
private Properties properties = new Properties(); private Properties properties = new Properties();
private boolean debugInformationGenerated; private boolean debugInformationGenerated;
private boolean sourceMapsFileGenerated; private boolean sourceMapsFileGenerated;
@ -139,6 +140,10 @@ public class TeaVMTool {
this.mainClass = mainClass; this.mainClass = mainClass;
} }
public void setEntryPointName(String entryPointName) {
this.entryPointName = entryPointName;
}
public boolean isDebugInformationGenerated() { public boolean isDebugInformationGenerated() {
return debugInformationGenerated; return debugInformationGenerated;
} }
@ -367,7 +372,7 @@ public class TeaVMTool {
vm.add(transformer); vm.add(transformer);
} }
if (mainClass != null) { if (mainClass != null) {
vm.entryPoint(mainClass); vm.entryPoint(mainClass, entryPointName);
} }
for (String className : classesToPreserve) { for (String className : classesToPreserve) {
vm.preserveType(className); vm.preserveType(className);

View File

@ -38,6 +38,8 @@ public interface BuildStrategy {
void setMainClass(String mainClass); void setMainClass(String mainClass);
void setEntryPointName(String entryPointName);
void setTargetDirectory(String targetDirectory); void setTargetDirectory(String targetDirectory);
void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated); void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated);

View File

@ -44,6 +44,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
private List<String> classPathEntries = new ArrayList<>(); private List<String> classPathEntries = new ArrayList<>();
private TeaVMTargetType targetType; private TeaVMTargetType targetType;
private String mainClass; private String mainClass;
private String entryPointName;
private String targetDirectory; private String targetDirectory;
private String targetFileName = ""; private String targetFileName = "";
private boolean incremental; private boolean incremental;
@ -98,6 +99,11 @@ public class InProcessBuildStrategy implements BuildStrategy {
this.mainClass = mainClass; this.mainClass = mainClass;
} }
@Override
public void setEntryPointName(String entryPointName) {
this.entryPointName = entryPointName;
}
@Override @Override
public void setTargetDirectory(String targetDirectory) { public void setTargetDirectory(String targetDirectory) {
this.targetDirectory = targetDirectory; this.targetDirectory = targetDirectory;
@ -191,6 +197,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
tool.setLog(log); tool.setLog(log);
tool.setTargetType(targetType); tool.setTargetType(targetType);
tool.setMainClass(mainClass); tool.setMainClass(mainClass);
tool.setEntryPointName(entryPointName);
tool.setTargetDirectory(new File(targetDirectory)); tool.setTargetDirectory(new File(targetDirectory));
tool.setTargetFileName(targetFileName); tool.setTargetFileName(targetFileName);
tool.setClassLoader(buildClassLoader()); tool.setClassLoader(buildClassLoader());

View File

@ -78,6 +78,11 @@ public class RemoteBuildStrategy implements BuildStrategy {
request.mainClass = mainClass; request.mainClass = mainClass;
} }
@Override
public void setEntryPointName(String entryPointName) {
request.entryPointName = entryPointName;
}
@Override @Override
public void setTargetDirectory(String targetDirectory) { public void setTargetDirectory(String targetDirectory) {
request.targetDirectory = targetDirectory; request.targetDirectory = targetDirectory;
@ -219,7 +224,7 @@ public class RemoteBuildStrategy implements BuildStrategy {
private TeaVMProgressListener listener; private TeaVMProgressListener listener;
private TeaVMToolLog log; private TeaVMToolLog log;
public CallbackImpl(TeaVMProgressListener listener, TeaVMToolLog log) throws RemoteException { CallbackImpl(TeaVMProgressListener listener, TeaVMToolLog log) throws RemoteException {
super(); super();
this.listener = listener; this.listener = listener;
this.log = log; this.log = log;

View File

@ -141,6 +141,7 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
} }
tool.setTargetType(request.targetType); tool.setTargetType(request.targetType);
tool.setMainClass(request.mainClass); tool.setMainClass(request.mainClass);
tool.setEntryPointName(request.entryPointName);
tool.setTargetDirectory(new File(request.targetDirectory)); tool.setTargetDirectory(new File(request.targetDirectory));
tool.setTargetFileName(request.tagetFileName); tool.setTargetFileName(request.tagetFileName);
tool.setClassLoader(buildClassLoader(request.classPath, incremental && request.incremental)); tool.setClassLoader(buildClassLoader(request.classPath, incremental && request.incremental));

View File

@ -31,6 +31,7 @@ public class RemoteBuildRequest implements Serializable {
public String[] classesToPreserve; public String[] classesToPreserve;
public TeaVMTargetType targetType; public TeaVMTargetType targetType;
public String mainClass; public String mainClass;
public String entryPointName;
public String targetDirectory; public String targetDirectory;
public String tagetFileName = ""; public String tagetFileName = "";
public boolean sourceMapsFileGenerated; public boolean sourceMapsFileGenerated;

View File

@ -61,6 +61,12 @@
<artifactId>teavm-core</artifactId> <artifactId>teavm-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso-apis</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.teavm</groupId> <groupId>org.teavm</groupId>
<artifactId>teavm-tooling</artifactId> <artifactId>teavm-tooling</artifactId>
@ -100,6 +106,41 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.teavm</groupId>
<artifactId>teavm-maven-plugin</artifactId>
<version>${project.version}</version>
<dependencies>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso-impl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-classlib</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>compile-deobfuscator</id>
<goals>
<goal>compile</goal>
</goals>
<phase>process-classes</phase>
<configuration>
<targetDirectory>${build.directory}/classes/teavm/devserver</targetDirectory>
<targetFileName>deobfuscator.js</targetFileName>
<minifying>true</minifying>
<optimizationLevel>ADVANCED</optimizationLevel>
<mainClass>org.teavm.devserver.deobfuscate.Deobfuscator</mainClass>
<entryPointName>$teavm_deobfuscator</entryPointName>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>

View File

@ -97,9 +97,11 @@ public class CodeServlet extends HttpServlet {
private String fileName = "classes.js"; private String fileName = "classes.js";
private String pathToFile = "/"; private String pathToFile = "/";
private String indicatorWsPath; private String indicatorWsPath;
private String deobfuscatorPath;
private List<String> sourcePath = new ArrayList<>(); private List<String> sourcePath = new ArrayList<>();
private TeaVMToolLog log = new EmptyTeaVMToolLog(); private TeaVMToolLog log = new EmptyTeaVMToolLog();
private boolean indicator; private boolean indicator;
private boolean deobfuscateStack;
private boolean automaticallyReloaded; private boolean automaticallyReloaded;
private int port; private int port;
private int debugPort; private int debugPort;
@ -163,6 +165,10 @@ public class CodeServlet extends HttpServlet {
this.indicator = indicator; this.indicator = indicator;
} }
public void setDeobfuscateStack(boolean deobfuscateStack) {
this.deobfuscateStack = deobfuscateStack;
}
public void setPort(int port) { public void setPort(int port) {
this.port = port; this.port = port;
} }
@ -263,6 +269,7 @@ public class CodeServlet extends HttpServlet {
} }
indicatorWsPath = pathToFile + fileName + ".ws"; indicatorWsPath = pathToFile + fileName + ".ws";
deobfuscatorPath = pathToFile + fileName + ".deobfuscator.js";
WebSocketPolicy wsPolicy = new WebSocketPolicy(WebSocketBehavior.SERVER); WebSocketPolicy wsPolicy = new WebSocketPolicy(WebSocketBehavior.SERVER);
wsFactory = WebSocketServletFactory.Loader.load(config.getServletContext(), wsPolicy); wsFactory = WebSocketServletFactory.Loader.load(config.getServletContext(), wsPolicy);
wsFactory.setCreator((req, resp) -> { wsFactory.setCreator((req, resp) -> {
@ -304,6 +311,9 @@ public class CodeServlet extends HttpServlet {
return; return;
} }
} }
} else if (path.equals(deobfuscatorPath)) {
serveDeobfuscator(resp);
return;
} else { } else {
byte[] fileContent; byte[] fileContent;
boolean firstTime; boolean firstTime;
@ -340,6 +350,17 @@ public class CodeServlet extends HttpServlet {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND); resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
} }
private void serveDeobfuscator(HttpServletResponse resp) throws IOException {
ClassLoader loader = CodeServlet.class.getClassLoader();
resp.setStatus(HttpServletResponse.SC_OK);
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/plain");
try (InputStream input = loader.getResourceAsStream("teavm/devserver/deobfuscator.js")) {
IOUtils.copy(input, resp.getOutputStream());
}
resp.getOutputStream().flush();
}
private void proxy(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException { private void proxy(HttpServletRequest req, HttpServletResponse resp, String path) throws IOException {
AsyncContext async = req.startAsync(); AsyncContext async = req.startAsync();
@ -763,9 +784,11 @@ public class CodeServlet extends HttpServlet {
script = script.replace("WS_PATH", "localhost:" + port + pathToFile + fileName + ".ws"); script = script.replace("WS_PATH", "localhost:" + port + pathToFile + fileName + ".ws");
script = script.replace("BOOT_FLAG", Boolean.toString(boot)); script = script.replace("BOOT_FLAG", Boolean.toString(boot));
script = script.replace("RELOAD_FLAG", Boolean.toString(automaticallyReloaded)); script = script.replace("RELOAD_FLAG", Boolean.toString(automaticallyReloaded));
script = script.replace("FILE_NAME", "http://localhost:" + port + pathToFile + fileName);
script = script.replace("INDICATOR_FLAG", Boolean.toString(indicator)); script = script.replace("INDICATOR_FLAG", Boolean.toString(indicator));
script = script.replace("DEBUG_PORT", Integer.toString(debugPort)); script = script.replace("DEBUG_PORT", Integer.toString(debugPort));
script = script.replace("FILE_NAME", "\"" + fileName + "\"");
script = script.replace("PATH_TO_FILE", "\"http://localhost:" + port + pathToFile + "\"");
script = script.replace("DEOBFUSCATE_FLAG", String.valueOf(deobfuscateStack));
return script; return script;
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("IO error occurred writing debug information", e); throw new RuntimeException("IO error occurred writing debug information", e);

View File

@ -30,6 +30,7 @@ public class DevServer {
private String fileName = "classes.js"; private String fileName = "classes.js";
private List<String> sourcePath = new ArrayList<>(); private List<String> sourcePath = new ArrayList<>();
private boolean indicator; private boolean indicator;
private boolean deobfuscateStack;
private boolean reloadedAutomatically; private boolean reloadedAutomatically;
private TeaVMToolLog log; private TeaVMToolLog log;
private CodeServlet servlet; private CodeServlet servlet;
@ -79,6 +80,10 @@ public class DevServer {
this.indicator = indicator; this.indicator = indicator;
} }
public void setDeobfuscateStack(boolean deobfuscateStack) {
this.deobfuscateStack = deobfuscateStack;
}
public void setReloadedAutomatically(boolean reloadedAutomatically) { public void setReloadedAutomatically(boolean reloadedAutomatically) {
this.reloadedAutomatically = reloadedAutomatically; this.reloadedAutomatically = reloadedAutomatically;
} }
@ -126,6 +131,7 @@ public class DevServer {
servlet.setLog(log); servlet.setLog(log);
servlet.getSourcePath().addAll(sourcePath); servlet.getSourcePath().addAll(sourcePath);
servlet.setIndicator(indicator); servlet.setIndicator(indicator);
servlet.setDeobfuscateStack(deobfuscateStack);
servlet.setAutomaticallyReloaded(reloadedAutomatically); servlet.setAutomaticallyReloaded(reloadedAutomatically);
servlet.setPort(port); servlet.setPort(port);
servlet.setDebugPort(debugPort); servlet.setDebugPort(debugPort);

View File

@ -0,0 +1,24 @@
/*
* Copyright 2019 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.
*/
package org.teavm.devserver.deobfuscate;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
@JSFunctor
public interface DeobfuscateFunction extends JSObject {
Frame[] apply(String stack);
}

View File

@ -0,0 +1,148 @@
/*
* Copyright 2019 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.
*/
package org.teavm.devserver.deobfuscate;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.teavm.debugging.information.DebugInformation;
import org.teavm.debugging.information.SourceLocation;
import org.teavm.jso.JSBody;
import org.teavm.jso.ajax.XMLHttpRequest;
import org.teavm.jso.core.JSArray;
import org.teavm.jso.core.JSRegExp;
import org.teavm.jso.core.JSString;
import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.Int8Array;
import org.teavm.model.MethodReference;
public final class Deobfuscator {
private static final JSRegExp FRAME_PATTERN = JSRegExp.create("^ +at ([^(]+) *\\((.+):([0-9]+):([0-9]+)\\) *$");
private Deobfuscator() {
}
public static void main(String[] args) {
loadDeobfuscator(args[0], args[1]);
}
private static void loadDeobfuscator(String fileName, String classesFileName) {
XMLHttpRequest xhr = XMLHttpRequest.create();
xhr.setResponseType("arraybuffer");
xhr.onComplete(() -> {
installDeobfuscator(xhr.getResponse().cast(), classesFileName);
});
xhr.open("GET", fileName);
xhr.send();
}
private static void installDeobfuscator(ArrayBuffer buffer, String classesFileName) {
Int8Array array = Int8Array.create(buffer);
DebugInformation debugInformation;
try {
debugInformation = DebugInformation.read(new Int8ArrayInputStream(array));
} catch (IOException e) {
e.printStackTrace();
return;
}
setDeobfuscateFunction(stack -> {
List<Frame> frames = new ArrayList<>();
for (String line : splitLines(stack)) {
JSArray<JSString> groups = FRAME_PATTERN.exec(JSString.valueOf(line));
if (groups == null) {
continue;
}
String functionName = groups.get(1).stringValue();
String fileName = groups.get(2).stringValue();
int lineNumber = Integer.parseInt(groups.get(3).stringValue());
int columnNumber = Integer.parseInt(groups.get(4).stringValue());
Frame frame = deobfuscateFrame(debugInformation, classesFileName, fileName, lineNumber, columnNumber);
if (frame == null) {
frame = createDefaultFrame(fileName, functionName, lineNumber);
}
frames.add(frame);
}
return frames.toArray(new Frame[0]);
});
DeobfuscatorCallback callback = getCallback();
if (callback != null) {
callback.run();
}
}
private static Frame deobfuscateFrame(DebugInformation debugInformation, String classesFileName,
String fileName, int lineNumber, int columnNumber) {
if (!fileName.equals(classesFileName)) {
return null;
}
MethodReference method = debugInformation.getMethodAt(lineNumber - 1, columnNumber - 1);
if (method == null) {
return null;
}
SourceLocation location = debugInformation.getSourceLocation(lineNumber - 1, columnNumber - 1);
String decodedFileName = location != null ? location.getFileName() : null;
if (decodedFileName != null) {
decodedFileName = decodedFileName.substring(decodedFileName.lastIndexOf('/') + 1);
}
Frame frame = createEmptyFrame();
frame.setClassName(method.getClassName());
frame.setMethodName(method.getName());
frame.setFileName(decodedFileName);
if (location != null) {
frame.setLineNumber(location.getLine());
}
return frame;
}
private static Frame createDefaultFrame(String fileName, String functionName, int lineNumber) {
Frame frame = createEmptyFrame();
frame.setFileName(fileName);
frame.setMethodName(functionName != null ? functionName : "<unknown function>");
frame.setClassName("<JS>");
frame.setLineNumber(lineNumber);
return frame;
}
private static String[] splitLines(String text) {
List<String> result = new ArrayList<>();
int index = 0;
while (index < text.length()) {
int next = text.indexOf('\n', index);
if (next < 0) {
next = text.length();
}
result.add(text.substring(index, next));
index = next + 1;
}
return result.toArray(new String[0]);
}
@JSBody(script = "return {};")
private static native Frame createEmptyFrame();
@JSBody(params = "f", script = "window.$rt_decodeStack = f;")
private static native void setDeobfuscateFunction(DeobfuscateFunction f);
@JSBody(script = "return typeof $teavm_deobfuscator_callback === 'function'"
+ "? $teavm_deobfuscator_callback : null;")
private static native DeobfuscatorCallback getCallback();
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2019 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.
*/
package org.teavm.devserver.deobfuscate;
import org.teavm.jso.JSFunctor;
import org.teavm.jso.JSObject;
@JSFunctor
public interface DeobfuscatorCallback extends JSObject {
void run();
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2019 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.
*/
package org.teavm.devserver.deobfuscate;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
public interface Frame extends JSObject {
@JSProperty
void setClassName(String className);
@JSProperty
void setFileName(String fileName);
@JSProperty
void setMethodName(String methodName);
@JSProperty
void setLineNumber(int lineNumber);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2019 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.
*/
package org.teavm.devserver.deobfuscate;
import java.io.InputStream;
import org.teavm.jso.typedarrays.Int8Array;
public class Int8ArrayInputStream extends InputStream {
private Int8Array array;
private int pos;
public Int8ArrayInputStream(Int8Array array) {
this.array = array;
}
@Override
public int read() {
if (pos >= array.getLength()) {
return -1;
}
return array.get(pos++) & 0xFF;
}
@Override
public int read(byte[] b, int off, int len) {
if (pos >= array.getLength()) {
return -1;
}
int count = Math.min(len, array.getLength() - pos);
for (int i = 0; i < count; ++i) {
b[off++] = array.get(pos++);
}
return count;
}
}

View File

@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
(function () { (function (window) {
var boot = BOOT_FLAG; var boot = BOOT_FLAG;
var reload = RELOAD_FLAG; var reload = RELOAD_FLAG;
var indicatorVisible = INDICATOR_FLAG; var indicatorVisible = INDICATOR_FLAG;
var debugPort = DEBUG_PORT; var debugPort = DEBUG_PORT;
var deobfuscate = DEOBFUSCATE_FLAG;
var fileName = FILE_NAME;
var pathToFile = PATH_TO_FILE;
function createWebSocket() { function createWebSocket() {
return new WebSocket("ws://WS_PATH"); return new WebSocket("ws://WS_PATH");
@ -128,10 +131,37 @@
} }
} }
function installDeobfuscator() {
if (!deobfuscate) {
return;
}
if (typeof main === 'function') {
var oldMain = main;
main = function() {
var args = arguments;
window.$teavm_deobfuscator_callback = function() {
oldMain.apply(window, args);
};
var elem = document.createElement("script");
elem.src = pathToFile + fileName + ".deobfuscator.js";
elem.onload = function() {
$teavm_deobfuscator([pathToFile + fileName + ".teavmdbg", pathToFile + fileName]);
};
document.head.append(elem);
};
}
}
if (!boot) {
installDeobfuscator();
}
function startMain() { function startMain() {
ws.close(); ws.close();
window.removeEventListener("load", onLoad); window.removeEventListener("load", onLoad);
document.body.removeChild(indicator.container); document.body.removeChild(indicator.container);
installDeobfuscator();
main(); main();
} }
@ -150,7 +180,7 @@
window.location.reload(true); window.location.reload(true);
} else if (boot) { } else if (boot) {
var scriptElem = document.createElement("script"); var scriptElem = document.createElement("script");
scriptElem.src = "FILE_NAME"; scriptElem.src = pathToFile + fileName;
scriptElem.onload = startMain; scriptElem.onload = startMain;
document.head.appendChild(scriptElem); document.head.appendChild(scriptElem);
} }
@ -181,4 +211,4 @@
window.addEventListener("message", connectDebugAgent); window.addEventListener("message", connectDebugAgent);
window.postMessage({teavmDebugger: {port: debugPort}}, "*"); window.postMessage({teavmDebugger: {port: debugPort}}, "*");
} }
})(); })(this);

View File

@ -22,6 +22,7 @@ public class DevServerConfiguration {
public String[] classPath; public String[] classPath;
public String[] sourcePath; public String[] sourcePath;
public boolean indicator; public boolean indicator;
public boolean deobfuscateStack;
public boolean autoReload; public boolean autoReload;
public int port; public int port;
public String pathToFile; public String pathToFile;

View File

@ -122,6 +122,9 @@ public class DevServerRunner extends UnicastRemoteObject implements DevServerMan
case "-i": case "-i":
server.setIndicator(true); server.setIndicator(true);
break; break;
case "-deobf":
server.setDeobfuscateStack(true);
break;
case "-a": case "-a":
server.setReloadedAutomatically(true); server.setReloadedAutomatically(true);
break; break;
@ -178,6 +181,9 @@ public class DevServerRunner extends UnicastRemoteObject implements DevServerMan
if (options.indicator) { if (options.indicator) {
arguments.add("-i"); arguments.add("-i");
} }
if (options.deobfuscateStack) {
arguments.add("-deobf");
}
if (options.autoReload) { if (options.autoReload) {
arguments.add("-a"); arguments.add("-a");
} }

View File

@ -44,6 +44,7 @@ public class TeaVMDevServerConfiguration extends ModuleBasedConfiguration<RunCon
private String pathToFile = ""; private String pathToFile = "";
private String fileName = "classes.js"; private String fileName = "classes.js";
private boolean indicator = true; private boolean indicator = true;
private boolean deobfuscateStack = true;
private boolean automaticallyReloaded; private boolean automaticallyReloaded;
private int maxHeap = 1024; private int maxHeap = 1024;
private String proxyUrl = ""; private String proxyUrl = "";
@ -155,6 +156,16 @@ public class TeaVMDevServerConfiguration extends ModuleBasedConfiguration<RunCon
this.indicator = indicator; this.indicator = indicator;
} }
@Property
@Tag
public boolean isDeobfuscateStack() {
return deobfuscateStack;
}
public void setDeobfuscateStack(boolean deobfuscateStack) {
this.deobfuscateStack = deobfuscateStack;
}
@Property @Property
@Tag @Tag
public boolean isAutomaticallyReloaded() { public boolean isAutomaticallyReloaded() {

View File

@ -78,6 +78,7 @@ public class TeaVMDevServerRunState implements RunProfileState {
config.fileName = configuration.getFileName(); config.fileName = configuration.getFileName();
config.port = configuration.getPort(); config.port = configuration.getPort();
config.indicator = configuration.isIndicator(); config.indicator = configuration.isIndicator();
config.deobfuscateStack = configuration.isDeobfuscateStack();
config.autoReload = configuration.isAutomaticallyReloaded(); config.autoReload = configuration.isAutomaticallyReloaded();
config.mainClass = configuration.getMainClass(); config.mainClass = configuration.getMainClass();
config.maxHeap = configuration.getMaxHeap(); config.maxHeap = configuration.getMaxHeap();

View File

@ -48,6 +48,7 @@ public class TeaVMDevServerSettingsPanel extends JPanel {
private JTextField pathToFileField; private JTextField pathToFileField;
private JTextField fileNameField; private JTextField fileNameField;
private JCheckBox indicatorField; private JCheckBox indicatorField;
private JCheckBox deobfuscateStackField;
private JCheckBox autoReloadField; private JCheckBox autoReloadField;
private JFormattedTextField maxHeapField; private JFormattedTextField maxHeapField;
private JTextField proxyUrlField; private JTextField proxyUrlField;
@ -76,6 +77,7 @@ public class TeaVMDevServerSettingsPanel extends JPanel {
fileNameField = new JTextField(); fileNameField = new JTextField();
pathToFileField = new JTextField(); pathToFileField = new JTextField();
indicatorField = new JCheckBox("Display indicator on a web page:"); indicatorField = new JCheckBox("Display indicator on a web page:");
deobfuscateStackField = new JCheckBox("Deobfuscate stack traces in exceptions");
autoReloadField = new JCheckBox("Reload page automatically:"); autoReloadField = new JCheckBox("Reload page automatically:");
maxHeapField = new JFormattedTextField(new DecimalFormat("#0")); maxHeapField = new JFormattedTextField(new DecimalFormat("#0"));
@ -118,6 +120,7 @@ public class TeaVMDevServerSettingsPanel extends JPanel {
add(pathToFileField, constraints); add(pathToFileField, constraints);
add(indicatorField, constraints); add(indicatorField, constraints);
add(deobfuscateStackField, constraints);
add(autoReloadField, constraints); add(autoReloadField, constraints);
add(new JLabel("Server heap limit:"), labelConstraints); add(new JLabel("Server heap limit:"), labelConstraints);
@ -137,6 +140,7 @@ public class TeaVMDevServerSettingsPanel extends JPanel {
fileNameField.setText(configuration.getFileName()); fileNameField.setText(configuration.getFileName());
pathToFileField.setText(configuration.getPathToFile()); pathToFileField.setText(configuration.getPathToFile());
indicatorField.setSelected(configuration.isIndicator()); indicatorField.setSelected(configuration.isIndicator());
deobfuscateStackField.setSelected(configuration.isDeobfuscateStack());
autoReloadField.setSelected(configuration.isAutomaticallyReloaded()); autoReloadField.setSelected(configuration.isAutomaticallyReloaded());
maxHeapField.setText(Integer.toString(configuration.getMaxHeap())); maxHeapField.setText(Integer.toString(configuration.getMaxHeap()));
portField.setText(Integer.toString(configuration.getPort())); portField.setText(Integer.toString(configuration.getPort()));
@ -151,6 +155,7 @@ public class TeaVMDevServerSettingsPanel extends JPanel {
configuration.setFileName(fileNameField.getText().trim()); configuration.setFileName(fileNameField.getText().trim());
configuration.setPathToFile(pathToFileField.getText().trim()); configuration.setPathToFile(pathToFileField.getText().trim());
configuration.setIndicator(indicatorField.isSelected()); configuration.setIndicator(indicatorField.isSelected());
configuration.setDeobfuscateStack(deobfuscateStackField.isSelected());
configuration.setAutomaticallyReloaded(autoReloadField.isSelected()); configuration.setAutomaticallyReloaded(autoReloadField.isSelected());
if (!maxHeapField.getText().trim().isEmpty()) { if (!maxHeapField.getText().trim().isEmpty()) {
configuration.setMaxHeap(Integer.parseInt(maxHeapField.getText())); configuration.setMaxHeap(Integer.parseInt(maxHeapField.getText()));

View File

@ -110,6 +110,9 @@ public class TeaVMCompileMojo extends AbstractMojo {
@Parameter(property = "teavm.mainClass") @Parameter(property = "teavm.mainClass")
private String mainClass; private String mainClass;
@Parameter(property = "teavm.entryPointName", defaultValue = "main")
private String entryPointName;
@Parameter @Parameter
private String[] classesToPreserve; private String[] classesToPreserve;
@ -258,6 +261,7 @@ public class TeaVMCompileMojo extends AbstractMojo {
builder.setLog(toolLog); builder.setLog(toolLog);
try { try {
builder.setMainClass(mainClass); builder.setMainClass(mainClass);
builder.setEntryPointName(entryPointName);
if (!targetFileName.isEmpty()) { if (!targetFileName.isEmpty()) {
builder.setTargetFileName(targetFileName); builder.setTargetFileName(targetFileName);
} }