mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
JS: strip unused functions from hand-written runtime
This commit is contained in:
parent
485d23d675
commit
717bbf4a57
|
@ -462,15 +462,13 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
int start = sourceWriter.getOffset();
|
int start = sourceWriter.getOffset();
|
||||||
|
|
||||||
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter);
|
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter);
|
||||||
|
runtimeRenderer.prepareAstParts(renderer.isThreadLibraryUsed());
|
||||||
|
declarations.replay(runtimeRenderer.sink, RememberedSource.FILTER_REF);
|
||||||
|
epilogue.replay(runtimeRenderer.sink, RememberedSource.FILTER_REF);
|
||||||
|
runtimeRenderer.removeUnusedParts();
|
||||||
runtimeRenderer.renderRuntime();
|
runtimeRenderer.renderRuntime();
|
||||||
runtimeRenderer.renderHandWrittenRuntime("long.js");
|
|
||||||
if (renderer.isThreadLibraryUsed()) {
|
|
||||||
runtimeRenderer.renderHandWrittenRuntime("thread.js");
|
|
||||||
} else {
|
|
||||||
runtimeRenderer.renderHandWrittenRuntime("simpleThread.js");
|
|
||||||
}
|
|
||||||
declarations.write(sourceWriter, 0);
|
declarations.write(sourceWriter, 0);
|
||||||
runtimeRenderer.renderHandWrittenRuntime("array.js");
|
runtimeRenderer.renderEpilogue();
|
||||||
epilogue.write(sourceWriter, 0);
|
epilogue.write(sourceWriter, 0);
|
||||||
|
|
||||||
printWrapperEnd(sourceWriter);
|
printWrapperEnd(sourceWriter);
|
||||||
|
|
|
@ -20,16 +20,24 @@ import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import org.mozilla.javascript.CompilerEnvirons;
|
import org.mozilla.javascript.CompilerEnvirons;
|
||||||
import org.mozilla.javascript.Context;
|
import org.mozilla.javascript.Context;
|
||||||
import org.mozilla.javascript.ast.AstRoot;
|
import org.mozilla.javascript.ast.AstRoot;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
|
import org.teavm.backend.javascript.codegen.SourceWriterSink;
|
||||||
|
import org.teavm.backend.javascript.templating.AstRemoval;
|
||||||
|
import org.teavm.backend.javascript.templating.RemovablePartsFinder;
|
||||||
import org.teavm.backend.javascript.templating.TemplatingAstTransformer;
|
import org.teavm.backend.javascript.templating.TemplatingAstTransformer;
|
||||||
import org.teavm.backend.javascript.templating.TemplatingAstWriter;
|
import org.teavm.backend.javascript.templating.TemplatingAstWriter;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.vm.RenderingException;
|
import org.teavm.vm.RenderingException;
|
||||||
|
|
||||||
public class RuntimeRenderer {
|
public class RuntimeRenderer {
|
||||||
|
private final List<AstRoot> runtimeAstParts = new ArrayList<>();
|
||||||
|
private final List<AstRoot> epilogueAstParts = new ArrayList<>();
|
||||||
|
private final RemovablePartsFinder removablePartsFinder = new RemovablePartsFinder();
|
||||||
private final ClassReaderSource classSource;
|
private final ClassReaderSource classSource;
|
||||||
private final SourceWriter writer;
|
private final SourceWriter writer;
|
||||||
|
|
||||||
|
@ -38,15 +46,35 @@ public class RuntimeRenderer {
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderRuntime() throws RenderingException {
|
public void prepareAstParts(boolean threadLibraryUsed) {
|
||||||
renderHandWrittenRuntime("runtime.js");
|
runtimeAstParts.add(prepareAstPart("runtime.js"));
|
||||||
renderHandWrittenRuntime("intern.js");
|
runtimeAstParts.add(prepareAstPart("intern.js"));
|
||||||
|
runtimeAstParts.add(prepareAstPart("long.js"));
|
||||||
|
runtimeAstParts.add(prepareAstPart(threadLibraryUsed ? "thread.js" : "simpleThread.js"));
|
||||||
|
epilogueAstParts.add(prepareAstPart("array.js"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderHandWrittenRuntime(String name) {
|
public void renderRuntime() {
|
||||||
AstRoot ast = parseRuntime(name);
|
for (var ast : runtimeAstParts) {
|
||||||
|
renderHandWrittenRuntime(ast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderEpilogue() {
|
||||||
|
for (var ast : epilogueAstParts) {
|
||||||
|
renderHandWrittenRuntime(ast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AstRoot prepareAstPart(String name) {
|
||||||
|
var ast = parseRuntime(name);
|
||||||
ast.visit(new StringConstantElimination());
|
ast.visit(new StringConstantElimination());
|
||||||
new TemplatingAstTransformer(classSource).visit(ast);
|
new TemplatingAstTransformer(classSource).visit(ast);
|
||||||
|
removablePartsFinder.visit(ast);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderHandWrittenRuntime(AstRoot ast) {
|
||||||
var astWriter = new TemplatingAstWriter(writer, null, null);
|
var astWriter = new TemplatingAstWriter(writer, null, null);
|
||||||
astWriter.hoist(ast);
|
astWriter.hoist(ast);
|
||||||
astWriter.print(ast);
|
astWriter.print(ast);
|
||||||
|
@ -66,4 +94,22 @@ public class RuntimeRenderer {
|
||||||
throw new RenderingException(e);
|
throw new RenderingException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final SourceWriterSink sink = new SourceWriterSink() {
|
||||||
|
@Override
|
||||||
|
public SourceWriterSink appendFunction(String name) {
|
||||||
|
removablePartsFinder.markUsedDeclaration(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void removeUnusedParts() {
|
||||||
|
var removal = new AstRemoval(removablePartsFinder.getAllRemovableParts());
|
||||||
|
for (var part : runtimeAstParts) {
|
||||||
|
removal.visit(part);
|
||||||
|
}
|
||||||
|
for (var part : epilogueAstParts) {
|
||||||
|
removal.visit(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.backend.javascript.templating;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import org.mozilla.javascript.ast.AstNode;
|
||||||
|
import org.mozilla.javascript.ast.ExpressionStatement;
|
||||||
|
import org.mozilla.javascript.ast.VariableDeclaration;
|
||||||
|
import org.teavm.backend.javascript.ast.AstVisitor;
|
||||||
|
|
||||||
|
public class AstRemoval extends AstVisitor {
|
||||||
|
private Set<AstNode> nodes;
|
||||||
|
|
||||||
|
public AstRemoval(Set<AstNode> nodes) {
|
||||||
|
this.nodes = nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ExpressionStatement node) {
|
||||||
|
if (nodes.contains(node.getExpression())) {
|
||||||
|
replaceWith(null);
|
||||||
|
} else {
|
||||||
|
super.visit(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(VariableDeclaration node) {
|
||||||
|
for (var iter = node.getVariables().iterator(); iter.hasNext();) {
|
||||||
|
var initializer = iter.next();
|
||||||
|
if (nodes.contains(initializer)) {
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node.getVariables().isEmpty()) {
|
||||||
|
replaceWith(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.backend.javascript.templating;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.mozilla.javascript.ast.Assignment;
|
||||||
|
import org.mozilla.javascript.ast.AstNode;
|
||||||
|
import org.mozilla.javascript.ast.ElementGet;
|
||||||
|
import org.mozilla.javascript.ast.ExpressionStatement;
|
||||||
|
import org.mozilla.javascript.ast.FunctionNode;
|
||||||
|
import org.mozilla.javascript.ast.Name;
|
||||||
|
import org.mozilla.javascript.ast.PropertyGet;
|
||||||
|
import org.mozilla.javascript.ast.Scope;
|
||||||
|
import org.mozilla.javascript.ast.VariableDeclaration;
|
||||||
|
import org.teavm.backend.javascript.ast.AstVisitor;
|
||||||
|
|
||||||
|
public class RemovablePartsFinder extends AstVisitor {
|
||||||
|
private Map<String, List<AstNode>> removableDeclarations = new HashMap<>();
|
||||||
|
private Map<String, Scope> removableDeclarationScopes = new HashMap<>();
|
||||||
|
private Map<String, Set<String>> dependencies = new HashMap<>();
|
||||||
|
private String insideDeclaration;
|
||||||
|
private boolean topLevel = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(FunctionNode node) {
|
||||||
|
if (topLevel) {
|
||||||
|
if (node.getName() != null && !node.getName().isEmpty()) {
|
||||||
|
removableDeclarations.computeIfAbsent(node.getName(), k -> new ArrayList<>()).add(node);
|
||||||
|
removableDeclarationScopes.put(node.getName(), scopeOfId(node.getName()));
|
||||||
|
}
|
||||||
|
topLevel = false;
|
||||||
|
insideDeclaration = node.getName();
|
||||||
|
visit(node.getBody());
|
||||||
|
insideDeclaration = null;
|
||||||
|
topLevel = true;
|
||||||
|
} else {
|
||||||
|
super.visit(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(VariableDeclaration node) {
|
||||||
|
if (topLevel) {
|
||||||
|
for (var initializer : node.getVariables()) {
|
||||||
|
var name = extractName(initializer.getTarget());
|
||||||
|
if (name != null) {
|
||||||
|
removableDeclarations.computeIfAbsent(name.getIdentifier(), k -> new ArrayList<>())
|
||||||
|
.add(initializer);
|
||||||
|
removableDeclarationScopes.put(name.getIdentifier(), scopeOfId(name.getIdentifier()));
|
||||||
|
if (initializer.getInitializer() != null) {
|
||||||
|
topLevel = false;
|
||||||
|
insideDeclaration = name.getIdentifier();
|
||||||
|
visit(initializer.getInitializer());
|
||||||
|
insideDeclaration = null;
|
||||||
|
topLevel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.visit(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ExpressionStatement node) {
|
||||||
|
if (topLevel && node.getExpression() instanceof Assignment) {
|
||||||
|
var assign = (Assignment) node.getExpression();
|
||||||
|
var name = extractName(assign.getLeft());
|
||||||
|
removableDeclarations.computeIfAbsent(name.getIdentifier(), k -> new ArrayList<>())
|
||||||
|
.add(node.getExpression());
|
||||||
|
removableDeclarationScopes.put(name.getIdentifier(), scopeOfId(name.getIdentifier()));
|
||||||
|
if (name != null) {
|
||||||
|
topLevel = false;
|
||||||
|
insideDeclaration = name.getIdentifier();
|
||||||
|
visit(assign.getRight());
|
||||||
|
insideDeclaration = null;
|
||||||
|
topLevel = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visit(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PropertyGet node) {
|
||||||
|
visit(node.getTarget());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Name node) {
|
||||||
|
if (scopeOfId(node.getIdentifier()) == removableDeclarationScopes.get(node.getIdentifier())
|
||||||
|
&& insideDeclaration != null) {
|
||||||
|
dependencies.computeIfAbsent(insideDeclaration, k -> new HashSet<>()).add(node.getIdentifier());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Name extractName(AstNode node) {
|
||||||
|
if (node instanceof Name) {
|
||||||
|
return (Name) node;
|
||||||
|
} else if (node instanceof PropertyGet) {
|
||||||
|
return extractName(((PropertyGet) node).getTarget());
|
||||||
|
} else if (node instanceof ElementGet) {
|
||||||
|
return extractName(((ElementGet) node).getTarget());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markUsedDeclaration(String name) {
|
||||||
|
removableDeclarations.remove(name);
|
||||||
|
var dependenciesToFollow = dependencies.remove(name);
|
||||||
|
if (dependenciesToFollow != null) {
|
||||||
|
for (var dependency : dependenciesToFollow) {
|
||||||
|
markUsedDeclaration(dependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<AstNode> getAllRemovableParts() {
|
||||||
|
var nodes = new HashSet<AstNode>();
|
||||||
|
for (var parts : removableDeclarations.values()) {
|
||||||
|
nodes.addAll(parts);
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user