JS: merge let statements to get better minification; rewrite some Array methods to generate less dependencies on runtime

This commit is contained in:
Alexey Andreev 2023-11-06 19:18:18 +01:00
parent 717bbf4a57
commit 21137c57a3
6 changed files with 140 additions and 43 deletions

View File

@ -15,6 +15,8 @@
*/
package org.teavm.classlib.java.lang.reflect;
import java.lang.reflect.Array;
import java.util.Set;
import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.backend.javascript.spi.Generator;
import org.teavm.backend.javascript.spi.GeneratorContext;
@ -31,6 +33,55 @@ public class ArrayNativeGenerator implements Generator {
@Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) {
if (methodRef.getName().equals("newInstanceImpl")) {
generateNewInstance(context, writer);
return;
}
template.builder(methodRef.getName()).withContext(context).build().write(writer, 0);
}
private void generateNewInstance(GeneratorContext context, SourceWriter writer) {
var dependency = context.getDependency().getMethod(new MethodReference(Array.class, "newInstance", Class.class,
int.class, Object.class));
template.builder("newInstanceImpl")
.withContext(context)
.withFragment("primitiveArrays", (w, p) -> {
w.append("switch").ws().append("(").append(context.getParameterName(1)).append(")")
.appendBlockStart();
var length = context.getParameterName(2);
var types = Set.of(dependency.getResult().getTypes());
if (types.contains("[Z")) {
writeArrayClause(w, "$rt_booleanArrayCls", "$rt_createBooleanArray", length);
}
if (types.contains("[B")) {
writeArrayClause(w, "$rt_byteArrayCls", "$rt_createByteArray", length);
}
if (types.contains("[C")) {
writeArrayClause(w, "$rt_charArrayCls", "$rt_createCharArray", length);
}
if (types.contains("[S")) {
writeArrayClause(w, "$rt_shortArrayCls", "$rt_createShortArray", length);
}
if (types.contains("[I")) {
writeArrayClause(w, "$rt_intArrayCls", "$rt_createIntArray", length);
}
if (types.contains("[J")) {
writeArrayClause(w, "$rt_longArrayCls", "$rt_createLongArray", length);
}
if (types.contains("~F")) {
writeArrayClause(w, "$rt_floatArrayCls", "$rt_createFloatArray", length);
}
if (types.contains("~D")) {
writeArrayClause(w, "$rt_doubleArrayCls", "$rt_createDoubleArray", length);
}
w.appendBlockEnd();
})
.build()
.write(writer, 0);
}
private void writeArrayClause(SourceWriter writer, String test, String construct, String length) {
writer.append("case ").appendFunction(test).append(":").ws().append("return ")
.appendFunction(construct).append("(").append(length).append(");").softNewLine();
}
}

View File

@ -23,16 +23,7 @@ function getLength(array) {
function newInstanceImpl(type, length) {
if (type.$meta.primitive) {
switch (type) {
case $rt_booleanArrayCls: return $rt_createBooleanArray(length);
case $rt_byteArrayCls: return $rt_createByteArray(length);
case $rt_shortArrayCls: return $rt_createShortArray(length);
case $rt_charArrayCls: return $rt_createCharArray(length);
case $rt_intArrayCls: return $rt_createIntArray(length);
case $rt_longArrayCls: return $rt_createLongArray(length);
case $rt_floatArrayCls: return $rt_createFloatArray(length);
case $rt_doubleArrayCls: return $rt_createDoubleArray(length);
}
teavm_fragment("primitiveArrays");
}
return $rt_createArray(type, length);
}

View File

@ -343,17 +343,9 @@ public class Renderer implements RenderingManager {
if (clinit != null && context.isDynamicInitializer(cls.getName())) {
renderCallClinit(clinit, cls);
}
if (!cls.hasModifier(ElementModifier.INTERFACE)
&& !cls.hasModifier(ElementModifier.ABSTRACT)) {
for (var method : cls.getMethods()) {
if (!method.hasModifier(ElementModifier.STATIC)) {
if (method.getName().equals("<init>")) {
renderInitializer(method);
}
}
}
}
var needsInitializers = !cls.hasModifier(ElementModifier.INTERFACE)
&& !cls.hasModifier(ElementModifier.ABSTRACT);
var hasLet = false;
for (var method : cls.getMethods()) {
if (!filterMethod(method)) {
@ -366,6 +358,11 @@ public class Renderer implements RenderingManager {
writer.append(",").newLine();
}
renderBody(method, decompiler);
if (needsInitializers && !method.hasModifier(ElementModifier.STATIC)
&& method.getName().equals("<init>")) {
writer.append(",").newLine();
renderInitializer(method);
}
}
if (hasLet) {
writer.append(";").newLine();
@ -699,7 +696,7 @@ public class Renderer implements RenderingManager {
private void renderInitializer(MethodReader method) {
MethodReference ref = method.getReference();
writer.emitMethod(ref.getDescriptor());
writer.append("let ").appendInit(ref).ws().append("=").ws();
writer.appendInit(ref).ws().append("=").ws();
if (ref.parameterCount() != 1) {
writer.append("(");
}
@ -724,8 +721,7 @@ public class Renderer implements RenderingManager {
}
writer.append(");").softNewLine();
writer.append("return " + instanceName + ";").softNewLine();
writer.outdent().append("};");
writer.newLine();
writer.outdent().append("}");
writer.emitMethod(null);
}

View File

@ -28,6 +28,7 @@ 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.LetJoiner;
import org.teavm.backend.javascript.templating.RemovablePartsFinder;
import org.teavm.backend.javascript.templating.TemplatingAstTransformer;
import org.teavm.backend.javascript.templating.TemplatingAstWriter;
@ -105,11 +106,14 @@ public class RuntimeRenderer {
public void removeUnusedParts() {
var removal = new AstRemoval(removablePartsFinder.getAllRemovableParts());
var letJoiner = new LetJoiner();
for (var part : runtimeAstParts) {
removal.visit(part);
letJoiner.visit(part);
}
for (var part : epilogueAstParts) {
removal.visit(part);
letJoiner.visit(part);
}
}
}

View File

@ -0,0 +1,71 @@
/*
* 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 org.mozilla.javascript.ast.AstNode;
import org.mozilla.javascript.ast.AstRoot;
import org.mozilla.javascript.ast.Block;
import org.mozilla.javascript.ast.FunctionNode;
import org.mozilla.javascript.ast.IfStatement;
import org.mozilla.javascript.ast.Scope;
import org.mozilla.javascript.ast.VariableDeclaration;
import org.teavm.backend.javascript.ast.AstVisitor;
public class LetJoiner extends AstVisitor {
@Override
public void visit(Block node) {
visitMany(node);
super.visit(node);
}
@Override
public void visit(Scope node) {
visitMany(node);
super.visit(node);
}
@Override
public void visit(AstRoot node) {
visitMany(node);
super.visit(node);
}
@Override
public void visit(FunctionNode node) {
visitMany(node.getBody());
super.visit(node);
}
private void visitMany(AstNode node) {
VariableDeclaration previous = null;
for (var childNode = node.getFirstChild(); childNode != null; ) {
var nextNode = childNode.getNext();
var child = (AstNode) childNode;
if (child instanceof VariableDeclaration) {
var decl = (VariableDeclaration) childNode;
if (previous != null && previous.getType() == decl.getType()) {
previous.getVariables().addAll(decl.getVariables());
node.removeChild(decl);
} else {
previous = decl;
}
} else {
previous = null;
}
childNode = nextNode;
}
}
}

View File

@ -145,7 +145,7 @@ let $rt_arraycls = cls => {
str += "]";
return str;
};
$rt_setCloneMethod(JavaArray.prototype, function() {
JavaArray.prototype[teavm_javaVirtualMethod('clone()Ljava/lang/Object;')] = function() {
let dataCopy;
if ('slice' in this.data) {
dataCopy = this.data.slice();
@ -156,7 +156,7 @@ let $rt_arraycls = cls => {
}
}
return new ($rt_arraycls(this.type))(dataCopy);
});
};
let name = "[" + cls.$meta.binaryName;
JavaArray.$meta = {
item: cls,
@ -694,22 +694,8 @@ let $dbg_class = obj => {
cls = cls.$meta.item;
}
let clsName = "";
if (cls === $rt_booleancls) {
clsName = "boolean";
} else if (cls === $rt_bytecls) {
clsName = "byte";
} else if (cls === $rt_shortcls) {
clsName = "short";
} else if (cls === $rt_charcls) {
clsName = "char";
} else if (cls === $rt_intcls) {
clsName = "int";
} else if (cls === $rt_longcl) {
clsName = "long";
} else if (cls === $rt_floatcls) {
clsName = "float";
} else if (cls === $rt_doublecls) {
clsName = "double";
if (cls.$meta.primitive) {
clsName = cls.$meta.name;
} else {
clsName = cls.$meta ? (cls.$meta.name || ("a/" + cls.name)) : "@" + cls.name;
}
@ -853,8 +839,6 @@ let $rt_substring = (string, start, end) => {
}
let $rt_substringSink = 0;
let $rt_setCloneMethod = (target, method) => target[teavm_javaVirtualMethod('clone()Ljava/lang/Object;')] = method;
let $rt_cls = (cls) => teavm_javaMethod("java.lang.Class",
"getClass(Lorg/teavm/platform/PlatformClass;)Ljava/lang/Class;")(cls);
let $rt_str = str => str === null ? null : teavm_javaConstructor("java.lang.String", "(Ljava/lang/Object;)V")(str);