Don't generate call to <clinit> on each static method during AST rendering. Instead, extract from WASM transformation that adds InitClassInstruction to beginning of each method and reuse in in JS. This fixes some issues in async methods, without making AST renderer much more complicated.

This commit is contained in:
Alexey Andreev 2017-02-22 23:05:58 +03:00
parent 323911ffc9
commit 891e3b8ea9
6 changed files with 69 additions and 37 deletions

View File

@ -67,6 +67,7 @@ import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.RaiseInstruction;
import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
import org.teavm.model.util.AsyncMethodFinder;
import org.teavm.model.util.ProgramUtils;
import org.teavm.vm.BuildTarget;
@ -87,6 +88,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
private MethodNodeCache astCache = new EmptyRegularMethodNodeCache();
private final Set<MethodReference> asyncMethods = new HashSet<>();
private final Set<MethodReference> asyncFamilyMethods = new HashSet<>();
private ClassInitializerInsertionTransformer clinitInsertionTransformer;
@Override
public List<ClassHolderTransformer> getTransformers() {
@ -101,6 +103,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
@Override
public void setController(TeaVMTargetController controller) {
this.controller = controller;
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
}
@Override
@ -213,6 +216,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
@Override
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
clinitInsertionTransformer.apply(method, program);
}
private void emit(ListableClassHolderSource classes, Writer writer, BuildTarget target) {

View File

@ -365,7 +365,7 @@ public class Renderer implements RenderingManager {
}
for (MethodNode method : cls.getMethods()) {
renderBody(method, clinit != null);
renderBody(method);
}
} catch (NamingException e) {
throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e);
@ -589,11 +589,7 @@ public class Renderer implements RenderingManager {
writer.append(");").ws().append("}");
}
private void renderBody(MethodNode method, boolean clinitNeeded) throws IOException {
boolean isClinit = (method.getModifiers().contains(ElementModifier.STATIC)
&& !method.getReference().getName().equals("<clinit>"))
|| method.getReference().getName().equals("<init>");
private void renderBody(MethodNode method) throws IOException {
StatementRenderer statementRenderer = new StatementRenderer(context, writer);
statementRenderer.setCurrentMethod(method);
@ -614,9 +610,6 @@ public class Renderer implements RenderingManager {
}
writer.append(")").ws().append("{").softNewLine().indent();
if (isClinit & clinitNeeded) {
writer.appendClass(method.getReference().getClassName()).append("_$callClinit();").softNewLine();
}
method.acceptVisitor(new MethodBodyRenderer(statementRenderer));
writer.outdent().append("}");
@ -640,7 +633,7 @@ public class Renderer implements RenderingManager {
private boolean async;
private StatementRenderer statementRenderer;
public MethodBodyRenderer(StatementRenderer statementRenderer) {
MethodBodyRenderer(StatementRenderer statementRenderer) {
this.statementRenderer = statementRenderer;
}

View File

@ -104,12 +104,12 @@ import org.teavm.model.ValueType;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.lowlevel.ClassInitializerEliminator;
import org.teavm.model.lowlevel.ClassInitializerTransformer;
import org.teavm.model.lowlevel.ShadowStackTransformer;
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
import org.teavm.runtime.Allocator;
import org.teavm.runtime.ExceptionHandling;
import org.teavm.runtime.RuntimeArray;
@ -127,10 +127,10 @@ public class WasmTarget implements TeaVMTarget {
private boolean debugging;
private boolean wastEmitted;
private boolean cEmitted;
private ClassInitializerInsertionTransformer clinitInsertionTransformer;
private ClassInitializerEliminator classInitializerEliminator;
private ClassInitializerTransformer classInitializerTransformer;
private ShadowStackTransformer shadowStackTransformer;
private MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
private WasmBinaryVersion version = WasmBinaryVersion.V_0xC;
@Override
@ -139,6 +139,7 @@ public class WasmTarget implements TeaVMTarget {
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
classInitializerTransformer = new ClassInitializerTransformer();
shadowStackTransformer = new ShadowStackTransformer(controller.getUnprocessedClassSource());
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
}
@Override
@ -261,30 +262,12 @@ public class WasmTarget implements TeaVMTarget {
@Override
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classes) {
ClassReader cls = classes.get(method.getOwnerName());
boolean hasClinit = cls.getMethod(clinitDescriptor) != null;
if (needsClinitCall(method) && hasClinit) {
BasicBlock entryBlock = program.basicBlockAt(0);
InitClassInstruction initInsn = new InitClassInstruction();
initInsn.setClassName(method.getOwnerName());
entryBlock.addFirst(initInsn);
}
clinitInsertionTransformer.apply(method, program);
classInitializerEliminator.apply(program);
classInitializerTransformer.transform(program);
shadowStackTransformer.apply(program, method);
}
private static boolean needsClinitCall(MethodReader method) {
if (method.getName().equals("<clinit>")) {
return false;
}
if (method.getName().equals("<init>")) {
return true;
}
return method.hasModifier(ElementModifier.STATIC);
}
@Override
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName)
throws IOException {

View File

@ -17,10 +17,6 @@ package org.teavm.model.instructions;
import org.teavm.model.Instruction;
/**
*
* @author Alexey Andreev
*/
public class InitClassInstruction extends Instruction {
private String className;

View File

@ -0,0 +1,55 @@
/*
* Copyright 2017 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.model.transformation;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.Program;
import org.teavm.model.instructions.InitClassInstruction;
public class ClassInitializerInsertionTransformer {
private static final MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
private ClassReaderSource classes;
public ClassInitializerInsertionTransformer(ClassReaderSource classes) {
this.classes = classes;
}
public void apply(MethodReader method, Program program) {
ClassReader cls = classes.get(method.getOwnerName());
boolean hasClinit = cls.getMethod(clinitDescriptor) != null;
if (needsClinitCall(method) && hasClinit) {
BasicBlock entryBlock = program.basicBlockAt(0);
InitClassInstruction initInsn = new InitClassInstruction();
initInsn.setClassName(method.getOwnerName());
entryBlock.addFirst(initInsn);
}
}
private static boolean needsClinitCall(MethodReader method) {
if (method.getName().equals("<clinit>")) {
return false;
}
if (method.getName().equals("<init>")) {
return true;
}
return method.hasModifier(ElementModifier.STATIC);
}
}

View File

@ -63,7 +63,6 @@ public class AsyncProgramSplitter {
Part initialPart = new Part(program.basicBlockCount());
initialPart.program = initialProgram;
parts.add(initialPart);
partMap.put(program.basicBlockAt(0).getFirstInstruction(), 0);
Step initialStep = new Step();
initialStep.source = 0;
initialStep.targetPart = initialPart;
@ -134,7 +133,7 @@ public class AsyncProgramSplitter {
// Continue with a new block in the new part
targetBlock = nextProgram.createBasicBlock();
if (step.source > 0) {
if (targetBlock.getIndex() > 0) {
JumpInstruction jumpToNextBlock = new JumpInstruction();
jumpToNextBlock.setTarget(targetBlock);
nextProgram.basicBlockAt(0).add(jumpToNextBlock);
@ -143,6 +142,8 @@ public class AsyncProgramSplitter {
}
step.targetPart = part;
part.originalBlocks[targetBlock.getIndex()] = step.source;
partMap.put(program.basicBlockAt(0).getFirstInstruction(), 0);
}
if (sourceBlock.getExceptionVariable() != null) {