Trying to reduce number of classes for which name is provided in their metadata

This commit is contained in:
Alexey Andreev 2020-03-02 16:36:09 +03:00
parent 46e786d957
commit ea1134d66b
24 changed files with 178 additions and 64 deletions

View File

@ -63,6 +63,8 @@ import org.teavm.vm.spi.TeaVMPlugin;
public class JCLPlugin implements TeaVMPlugin {
@Override
public void install(TeaVMHost host) {
host.add(new ObfuscationHacks());
if (!isBootstrap()) {
ServiceLoaderSupport serviceLoaderSupp = new ServiceLoaderSupport(host.getClassLoader());
host.add(serviceLoaderSupp);

View File

@ -0,0 +1,41 @@
/*
* Copyright 2020 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.classlib.impl;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
import org.teavm.model.util.ProgramUtils;
public class ObfuscationHacks implements ClassHolderTransformer {
@Override
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
if (cls.getName().equals("java.lang.Object")) {
if (context.isObfuscated() && !context.isStrict()) {
processObjectClass(cls);
}
}
}
private void processObjectClass(ClassHolder cls) {
MethodHolder toStringMethod = cls.getMethod(new MethodDescriptor("toString", String.class));
MethodHolder obfuscatedToStringMethod = cls.getMethod(
new MethodDescriptor("obfuscatedToString", String.class));
toStringMethod.setProgram(ProgramUtils.copy(obfuscatedToStringMethod.getProgram()));
}
}

View File

@ -74,13 +74,13 @@ public abstract class TEnum<E extends TEnum<E>> extends TObject implements TComp
// TODO: speed-up this method, use caching
T[] constants = enumType.getEnumConstants();
if (constants == null) {
throw new TIllegalArgumentException("Class does not represent enum: " + enumType.getName());
throw new TIllegalArgumentException("Class does not represent enum");
}
for (T constant : constants) {
if (constant.name().equals(name)) {
return constant;
}
}
throw new TIllegalArgumentException("Enum " + enumType.getName() + " does not have the " + name + "constant");
throw new TIllegalArgumentException("Enum does not have the " + name + "constant");
}
}

View File

@ -241,6 +241,10 @@ public class TObject {
return getClass().getName() + "@" + TInteger.toHexString(identity());
}
private String obfuscatedToString() {
return "<java_object>@" + Integer.toHexString(identity());
}
final int identity() {
if (PlatformDetector.isLowLevel()) {
Monitor monitor = this.monitor;

View File

@ -115,7 +115,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
"currentThread", Thread.class);
private TeaVMTargetController controller;
private boolean minifying = true;
private boolean obfuscated = true;
private boolean stackTraceIncluded;
private final Map<MethodReference, Generator> methodGenerators = new HashMap<>();
private final Map<MethodReference, Injector> methodInjectors = new HashMap<>();
@ -173,24 +173,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
injectorProviders.add(provider);
}
/**
* Reports whether this TeaVM instance uses obfuscation when generating the JavaScript code.
*
* @see #setMinifying(boolean)
* @return whether TeaVM produces obfuscated code.
*/
public boolean isMinifying() {
return minifying;
}
/**
* Specifies whether this TeaVM instance uses obfuscation when generating the JavaScript code.
*
* @see #isMinifying()
* @param minifying whether TeaVM should obfuscate code.
* @param obfuscated whether TeaVM should obfuscate code.
*/
public void setMinifying(boolean minifying) {
this.minifying = minifying;
public void setObfuscated(boolean obfuscated) {
this.obfuscated = obfuscated;
}
public MethodNodeCache getAstCache() {
@ -363,12 +352,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
return;
}
AliasProvider aliasProvider = minifying
AliasProvider aliasProvider = obfuscated
? new MinifyingAliasProvider(topLevelNameLimit)
: new DefaultAliasProvider(topLevelNameLimit);
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, controller.getUnprocessedClassSource());
SourceWriterBuilder builder = new SourceWriterBuilder(naming);
builder.setMinified(minifying);
builder.setMinified(obfuscated);
SourceWriter sourceWriter = builder.build(writer);
DebugInformationEmitter debugEmitterToUse = debugEmitter;
@ -382,12 +371,12 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming,
controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m),
controller.getClassInitializerInfo());
renderingContext.setMinifying(minifying);
renderingContext.setMinifying(obfuscated);
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
controller.getDiagnostics(), renderingContext);
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter);
renderer.setProperties(controller.getProperties());
renderer.setMinifying(minifying);
renderer.setMinifying(obfuscated);
renderer.setProgressConsumer(controller::reportProgress);
if (debugEmitter != null) {
for (PreparedClass preparedClass : clsNodes) {

View File

@ -114,6 +114,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
boolean asyncSupported;
private ReferenceCache referenceCache;
private Set<String> generatedClassNames = new HashSet<>();
DependencyType classType;
DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
Diagnostics diagnostics, ReferenceCache referenceCache) {
@ -140,6 +141,15 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
classCache = new CachedFunction<>(this::createClassDependency);
agent = new DependencyAgent(this);
classType = getType("java.lang.Class");
}
public void setObfuscated(boolean obfuscated) {
classSource.obfuscated = obfuscated;
}
public void setStrict(boolean strict) {
classSource.strict = strict;
}
public void setAsyncSupported(boolean asyncSupported) {

View File

@ -41,6 +41,8 @@ class DependencyClassSource implements ClassHolderSource {
private IncrementalDependencyRegistration dependencyRegistration;
private Map<String, ClassHolder> generatedClasses = new LinkedHashMap<>();
private List<ClassHolderTransformer> transformers = new ArrayList<>();
boolean obfuscated;
boolean strict;
Map<String, Optional<ClassHolder>> cache = new LinkedHashMap<>(1000, 0.5f);
DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics,
@ -128,5 +130,15 @@ class DependencyClassSource implements ClassHolderSource {
public IncrementalDependencyRegistration getIncrementalCache() {
return dependencyRegistration;
}
@Override
public boolean isObfuscated() {
return obfuscated;
}
@Override
public boolean isStrict() {
return strict;
}
};
}

View File

@ -271,8 +271,13 @@ public class DependencyNode implements ValueDependencyInfo {
if (connectWithoutChildNodes(node, filter)) {
connectArrayItemNodes(node);
if (classValueNode != null && classValueNode != this) {
classValueNode.connect(node.getClassValueNode());
if (classNodeParent == null) {
if (classValueNode != null && classValueNode != this) {
if (filter(dependencyAnalyzer.classType) && node.filter(dependencyAnalyzer.classType)
&& (filter == null || filter.match(dependencyAnalyzer.classType))) {
classValueNode.connect(node.getClassValueNode());
}
}
}
}
}
@ -354,15 +359,6 @@ public class DependencyNode implements ValueDependencyInfo {
return type instanceof ValueType.Array;
}
void connectClassValueNodesForDomain() {
if (typeSet == null) {
return;
}
for (DependencyNode node : typeSet.domain) {
node.connectClassValueNodes();
}
}
private void connectClassValueNodes() {
if (classNodeComplete) {
return;
@ -373,8 +369,18 @@ public class DependencyNode implements ValueDependencyInfo {
return;
}
if (!classNodeParent.filter(dependencyAnalyzer.classType)) {
return;
}
for (Transition transition : classNodeParent.transitionList.toArray(Transition.class)) {
connect(transition.destination.getClassValueNode());
if (transition.destination.classNodeParent != null) {
continue;
}
if (transition.destination.filter(dependencyAnalyzer.classType)
&& (transition.filter == null || transition.filter.match(dependencyAnalyzer.classType))) {
connect(transition.destination.getClassValueNode());
}
}
}
@ -400,6 +406,7 @@ public class DependencyNode implements ValueDependencyInfo {
public DependencyNode getClassValueNode() {
if (classValueNode == null) {
classValueNode = dependencyAnalyzer.createClassValueNode(degree, this);
classValueNode.connectClassValueNodes();
}
return classValueNode;
}
@ -484,7 +491,6 @@ public class DependencyNode implements ValueDependencyInfo {
for (DependencyNode node : domain) {
node.typeSet = typeSet;
}
connectClassValueNodesForDomain();
return;
}

View File

@ -58,10 +58,13 @@ class VirtualCallConsumer implements DependencyConsumer {
knownTypes.set(type.index);
String className = type.getName();
/*
if (DependencyAnalyzer.shouldLog) {
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". "
+ "Target class is " + className);
}
*/
if (className.startsWith("[")) {
className = "java.lang.Object";
}

View File

@ -24,4 +24,8 @@ public interface ClassHolderTransformerContext {
Diagnostics getDiagnostics();
IncrementalDependencyRegistration getIncrementalCache();
boolean isObfuscated();
boolean isStrict();
}

View File

@ -171,6 +171,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
classSourcePacker = builder.classSourcePacker;
dependencyAnalyzer = builder.dependencyAnalyzerFactory.create(builder.classSource, classLoader,
this, diagnostics, builder.referenceCache);
dependencyAnalyzer.setObfuscated(builder.obfuscated);
dependencyAnalyzer.setStrict(builder.strict);
progressListener = new TeaVMProgressListener() {
@Override public TeaVMProgressFeedback progressReached(int progress) {
return TeaVMProgressFeedback.CONTINUE;

View File

@ -30,6 +30,8 @@ public class TeaVMBuilder {
ReferenceCache referenceCache = new ReferenceCache();
DependencyAnalyzerFactory dependencyAnalyzerFactory = PreciseDependencyAnalyzer::new;
ClassSourcePacker classSourcePacker = (src, names) -> src;
boolean obfuscated;
boolean strict;
public TeaVMBuilder(TeaVMTarget target) {
this.target = target;
@ -74,6 +76,16 @@ public class TeaVMBuilder {
return this;
}
public TeaVMBuilder setObfuscated(boolean obfuscated) {
this.obfuscated = obfuscated;
return this;
}
public TeaVMBuilder setStrict(boolean strict) {
this.strict = strict;
return this;
}
public TeaVM build() {
return new TeaVM(this);
}

View File

@ -206,7 +206,7 @@ public class IncrementalTest {
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
vm.setProgramCache(programCache);
target.setAstCache(astCache);
target.setMinifying(false);
target.setObfuscated(false);
target.setStrict(true);
vm.add(new EntryPointTransformer(entryPoint));
vm.entryPoint(EntryPoint.class.getName());

View File

@ -334,6 +334,8 @@ public class IncrementalCBuilder {
.setClassSource(classSource)
.setDependencyAnalyzerFactory(FastDependencyAnalyzer::new)
.setClassSourcePacker(this::packClasses)
.setStrict(true)
.setObfuscated(false)
.build();
cTarget.setIncremental(true);

View File

@ -77,6 +77,10 @@ public final class TeaVMRunner {
.withDescription("causes TeaVM to generate minimized JavaScript file")
.withLongOpt("minify")
.create("m"));
options.addOption(OptionBuilder
.withDescription("causes TeaVM to produce code that is as close to Java semantics as possible "
+ "(in cost of performance)")
.create("strict"));
options.addOption(OptionBuilder
.withDescription("optimization level (1-3)")
.hasArg()
@ -185,7 +189,7 @@ public final class TeaVMRunner {
parsePreserveClassOptions();
parseOptimizationOption();
parseIncrementalOptions();
parseJavaScriptOptions();
parseGenerationOptions();
parseWasmOptions();
parseCOptions();
parseHeap();
@ -232,8 +236,9 @@ public final class TeaVMRunner {
}
}
private void parseJavaScriptOptions() {
tool.setMinifying(commandLine.hasOption("m"));
private void parseGenerationOptions() {
tool.setObfuscated(commandLine.hasOption("m"));
tool.setStrict(commandLine.hasOption("strict"));
if (commandLine.hasOption("max-toplevel-names")) {
try {

View File

@ -70,7 +70,8 @@ public class TeaVMTool {
private File targetDirectory = new File(".");
private TeaVMTargetType targetType = TeaVMTargetType.JAVASCRIPT;
private String targetFileName = "";
private boolean minifying = true;
private boolean obfuscated = true;
private boolean strict;
private int maxTopLevelNames = 10000;
private String mainClass;
private String entryPointName = "main";
@ -116,20 +117,16 @@ public class TeaVMTool {
this.targetDirectory = targetDirectory;
}
public String getTargetFileName() {
return targetFileName;
}
public void setTargetFileName(String targetFileName) {
this.targetFileName = targetFileName;
}
public boolean isMinifying() {
return minifying;
public void setObfuscated(boolean obfuscated) {
this.obfuscated = obfuscated;
}
public void setMinifying(boolean minifying) {
this.minifying = minifying;
public void setStrict(boolean strict) {
this.strict = strict;
}
public void setMaxTopLevelNames(int maxTopLevelNames) {
@ -314,7 +311,8 @@ public class TeaVMTool {
private TeaVMTarget prepareJavaScriptTarget() {
javaScriptTarget = new JavaScriptTarget();
javaScriptTarget.setMinifying(minifying);
javaScriptTarget.setObfuscated(obfuscated);
javaScriptTarget.setStrict(strict);
javaScriptTarget.setTopLevelNameLimit(maxTopLevelNames);
debugEmitter = debugInformationGenerated || sourceMapsFileGenerated
@ -332,7 +330,7 @@ public class TeaVMTool {
webAssemblyTarget.setVersion(wasmVersion);
webAssemblyTarget.setMinHeapSize(minHeapSize);
webAssemblyTarget.setMaxHeapSize(maxHeapSize);
webAssemblyTarget.setObfuscated(minifying);
webAssemblyTarget.setObfuscated(obfuscated);
return webAssemblyTarget;
}
@ -343,7 +341,7 @@ public class TeaVMTool {
cTarget.setLineNumbersGenerated(debugInformationGenerated);
cTarget.setLongjmpUsed(longjmpSupported);
cTarget.setHeapDump(heapDump);
cTarget.setObfuscated(minifying);
cTarget.setObfuscated(obfuscated);
return cTarget;
}
@ -390,6 +388,8 @@ public class TeaVMTool {
vmBuilder.setDependencyAnalyzerFactory(fastDependencyAnalysis
? FastDependencyAnalyzer::new
: PreciseDependencyAnalyzer::new);
vmBuilder.setObfuscated(obfuscated);
vmBuilder.setStrict(strict);
vm = vmBuilder.build();
if (progressListener != null) {

View File

@ -52,7 +52,9 @@ public interface BuildStrategy {
void setIncremental(boolean incremental);
void setMinifying(boolean minifying);
void setObfuscated(boolean obfuscated);
void setStrict(boolean strict);
void setMaxTopLevelNames(int maxTopLevelNames);

View File

@ -51,7 +51,8 @@ public class InProcessBuildStrategy implements BuildStrategy {
private String cacheDirectory;
private TeaVMOptimizationLevel optimizationLevel = TeaVMOptimizationLevel.ADVANCED;
private boolean fastDependencyAnalysis;
private boolean minifying;
private boolean obfuscated;
private boolean strict;
private int maxTopLevelNames;
private boolean sourceMapsFileGenerated;
private boolean debugInformationGenerated;
@ -150,8 +151,13 @@ public class InProcessBuildStrategy implements BuildStrategy {
}
@Override
public void setMinifying(boolean minifying) {
this.minifying = minifying;
public void setObfuscated(boolean obfuscated) {
this.obfuscated = obfuscated;
}
@Override
public void setStrict(boolean strict) {
this.strict = strict;
}
@Override
@ -232,7 +238,8 @@ public class InProcessBuildStrategy implements BuildStrategy {
tool.setDebugInformationGenerated(debugInformationGenerated);
tool.setSourceFilesCopied(sourceFilesCopied);
tool.setMinifying(minifying);
tool.setObfuscated(obfuscated);
tool.setStrict(strict);
tool.setMaxTopLevelNames(maxTopLevelNames);
tool.setIncremental(incremental);
tool.getTransformers().addAll(Arrays.asList(transformers));

View File

@ -126,8 +126,13 @@ public class RemoteBuildStrategy implements BuildStrategy {
}
@Override
public void setMinifying(boolean minifying) {
request.minifying = minifying;
public void setObfuscated(boolean obfuscated) {
request.obfuscated = obfuscated;
}
@Override
public void setStrict(boolean strict) {
request.strict = strict;
}
@Override

View File

@ -155,7 +155,8 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
tool.setOptimizationLevel(request.optimizationLevel);
tool.setFastDependencyAnalysis(request.fastDependencyAnalysis);
tool.setMinifying(request.minifying);
tool.setObfuscated(request.obfuscated);
tool.setStrict(request.strict);
tool.setMaxTopLevelNames(request.maxTopLevelNames);
tool.setWasmVersion(request.wasmVersion);
tool.setMinHeapSize(request.minHeapSize);

View File

@ -39,7 +39,8 @@ public class RemoteBuildRequest implements Serializable {
public boolean sourceFilesCopied;
public boolean incremental;
public String cacheDirectory;
public boolean minifying;
public boolean obfuscated;
public boolean strict;
public int maxTopLevelNames;
public Properties properties;
public TeaVMOptimizationLevel optimizationLevel;

View File

@ -797,10 +797,12 @@ public class CodeServlet extends HttpServlet {
.setClassSource(classSource)
.setDependencyAnalyzerFactory(FastDependencyAnalyzer::new)
.setClassSourcePacker(this::packClasses)
.setStrict(true)
.setObfuscated(false)
.build();
jsTarget.setStackTraceIncluded(true);
jsTarget.setMinifying(false);
jsTarget.setObfuscated(false);
jsTarget.setAstCache(astCache);
jsTarget.setDebugEmitter(debugInformationBuilder);
jsTarget.setTopLevelNameLimit(2000);

View File

@ -42,7 +42,7 @@ interface TeaVMTestConfiguration<T extends TeaVMTarget> {
@Override
public void apply(JavaScriptTarget target) {
target.setMinifying(false);
target.setObfuscated(false);
}
};
@ -59,7 +59,7 @@ interface TeaVMTestConfiguration<T extends TeaVMTarget> {
@Override
public void apply(JavaScriptTarget target) {
target.setMinifying(false);
target.setObfuscated(false);
}
};
@ -76,7 +76,7 @@ interface TeaVMTestConfiguration<T extends TeaVMTarget> {
@Override
public void apply(JavaScriptTarget target) {
target.setMinifying(true);
target.setObfuscated(true);
}
};

View File

@ -80,6 +80,9 @@ public class TeaVMCompileMojo extends AbstractMojo {
@Parameter(property = "teavm.minifying", defaultValue = "true")
private boolean minifying = true;
@Parameter(property = "teavm.strict", defaultValue = "false")
private boolean strict;
@Parameter(property = "teavm.maxTopLevelNames", defaultValue = "10000")
private int maxTopLevelNames = 10000;
@ -159,7 +162,8 @@ public class TeaVMCompileMojo extends AbstractMojo {
builder.setLog(new MavenTeaVMToolLog(getLog()));
try {
builder.setClassPathEntries(prepareClassPath());
builder.setMinifying(minifying);
builder.setObfuscated(minifying);
builder.setStrict(strict);
builder.setMaxTopLevelNames(maxTopLevelNames);
builder.setTargetDirectory(targetDirectory.getAbsolutePath());
if (transformers != null) {