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();
|
||||
|
||||
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.renderHandWrittenRuntime("long.js");
|
||||
if (renderer.isThreadLibraryUsed()) {
|
||||
runtimeRenderer.renderHandWrittenRuntime("thread.js");
|
||||
} else {
|
||||
runtimeRenderer.renderHandWrittenRuntime("simpleThread.js");
|
||||
}
|
||||
declarations.write(sourceWriter, 0);
|
||||
runtimeRenderer.renderHandWrittenRuntime("array.js");
|
||||
runtimeRenderer.renderEpilogue();
|
||||
epilogue.write(sourceWriter, 0);
|
||||
|
||||
printWrapperEnd(sourceWriter);
|
||||
|
|
|
@ -20,16 +20,24 @@ import java.io.InputStream;
|
|||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.mozilla.javascript.CompilerEnvirons;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.ast.AstRoot;
|
||||
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.TemplatingAstWriter;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.vm.RenderingException;
|
||||
|
||||
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 SourceWriter writer;
|
||||
|
||||
|
@ -38,15 +46,35 @@ public class RuntimeRenderer {
|
|||
this.writer = writer;
|
||||
}
|
||||
|
||||
public void renderRuntime() throws RenderingException {
|
||||
renderHandWrittenRuntime("runtime.js");
|
||||
renderHandWrittenRuntime("intern.js");
|
||||
public void prepareAstParts(boolean threadLibraryUsed) {
|
||||
runtimeAstParts.add(prepareAstPart("runtime.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) {
|
||||
AstRoot ast = parseRuntime(name);
|
||||
public void renderRuntime() {
|
||||
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());
|
||||
new TemplatingAstTransformer(classSource).visit(ast);
|
||||
removablePartsFinder.visit(ast);
|
||||
return ast;
|
||||
}
|
||||
|
||||
private void renderHandWrittenRuntime(AstRoot ast) {
|
||||
var astWriter = new TemplatingAstWriter(writer, null, null);
|
||||
astWriter.hoist(ast);
|
||||
astWriter.print(ast);
|
||||
|
@ -66,4 +94,22 @@ public class RuntimeRenderer {
|
|||
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