From a8226ef6a3493787779cb9e4dd34b20819a704a9 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 11 Mar 2019 17:27:51 +0300 Subject: [PATCH] Decrease memory consumption --- .../backend/javascript/JavaScriptTarget.java | 2 +- .../org/teavm/cache/CachedAnnotation.java | 40 ++ .../org/teavm/cache/CachedAnnotations.java | 38 ++ .../org/teavm/cache/CachedClassReader.java | 67 +++ .../java/org/teavm/cache/CachedElement.java | 54 +++ .../java/org/teavm/cache/CachedField.java | 41 ++ .../java/org/teavm/cache/CachedMember.java | 27 ++ .../java/org/teavm/cache/CachedMethod.java | 97 ++++ .../main/java/org/teavm/cache/ClassIO.java | 160 ++++--- ....java => DiskCachedClassReaderSource.java} | 11 +- .../cache/MemoryCachedClassReaderSource.java | 52 ++- .../main/java/org/teavm/cache/ProgramIO.java | 415 +++++++++--------- .../java/org/teavm/cache/VarDataInput.java | 20 + .../java/org/teavm/cache/VarDataOutput.java | 5 + .../org/teavm/dependency/ClassDependency.java | 10 +- .../teavm/dependency/ClassSourcePacker.java | 23 + .../org/teavm/dependency/DependencyAgent.java | 2 +- .../teavm/dependency/DependencyAnalyzer.java | 33 +- .../dependency/DependencyClassSource.java | 6 +- .../dependency/FastDependencyAnalyzer.java | 4 +- .../org/teavm/dependency/FieldDependency.java | 10 +- .../teavm/dependency/MethodDependency.java | 11 +- core/src/main/java/org/teavm/vm/TeaVM.java | 10 +- .../main/java/org/teavm/vm/TeaVMBuilder.java | 7 + .../teavm/incremental/IncrementalTest.java | 2 +- .../java/org/teavm/tooling/TeaVMTool.java | 6 +- .../java/org/teavm/devserver/CodeServlet.java | 20 +- 27 files changed, 868 insertions(+), 305 deletions(-) create mode 100644 core/src/main/java/org/teavm/cache/CachedAnnotation.java create mode 100644 core/src/main/java/org/teavm/cache/CachedAnnotations.java create mode 100644 core/src/main/java/org/teavm/cache/CachedClassReader.java create mode 100644 core/src/main/java/org/teavm/cache/CachedElement.java create mode 100644 core/src/main/java/org/teavm/cache/CachedField.java create mode 100644 core/src/main/java/org/teavm/cache/CachedMember.java create mode 100644 core/src/main/java/org/teavm/cache/CachedMethod.java rename core/src/main/java/org/teavm/cache/{DiskCachedClassHolderSource.java => DiskCachedClassReaderSource.java} (93%) create mode 100644 core/src/main/java/org/teavm/dependency/ClassSourcePacker.java diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index a886c76bd..cd5f9d133 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -354,7 +354,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { renderingContext.setMinifying(minifying); Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, controller.getDiagnostics(), renderingContext); - RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter); + RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter); renderer.setProperties(controller.getProperties()); renderer.setMinifying(minifying); renderer.setProgressConsumer(controller::reportProgress); diff --git a/core/src/main/java/org/teavm/cache/CachedAnnotation.java b/core/src/main/java/org/teavm/cache/CachedAnnotation.java new file mode 100644 index 000000000..bd3f65324 --- /dev/null +++ b/core/src/main/java/org/teavm/cache/CachedAnnotation.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019 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.cache; + +import java.util.Map; +import org.teavm.model.AnnotationReader; +import org.teavm.model.AnnotationValue; + +class CachedAnnotation implements AnnotationReader { + String type; + Map fields; + + @Override + public String getType() { + return type; + } + + @Override + public AnnotationValue getValue(String fieldName) { + return fields.get(fieldName); + } + + @Override + public Iterable getAvailableFields() { + return fields.keySet(); + } +} diff --git a/core/src/main/java/org/teavm/cache/CachedAnnotations.java b/core/src/main/java/org/teavm/cache/CachedAnnotations.java new file mode 100644 index 000000000..dce13413c --- /dev/null +++ b/core/src/main/java/org/teavm/cache/CachedAnnotations.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019 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.cache; + +import java.util.Map; +import org.teavm.model.AnnotationContainerReader; +import org.teavm.model.AnnotationReader; + +class CachedAnnotations implements AnnotationContainerReader { + private Map data; + + public CachedAnnotations(Map data) { + this.data = data; + } + + @Override + public AnnotationReader get(String type) { + return data.get(type); + } + + @Override + public Iterable all() { + return data.values(); + } +} diff --git a/core/src/main/java/org/teavm/cache/CachedClassReader.java b/core/src/main/java/org/teavm/cache/CachedClassReader.java new file mode 100644 index 000000000..0675bbc28 --- /dev/null +++ b/core/src/main/java/org/teavm/cache/CachedClassReader.java @@ -0,0 +1,67 @@ +/* + * Copyright 2019 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.cache; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import org.teavm.model.ClassReader; +import org.teavm.model.FieldReader; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReader; + +class CachedClassReader extends CachedElement implements ClassReader { + String parent; + String owner; + Set interfaces; + Map methods; + Map fields; + + @Override + public String getParent() { + return parent; + } + + @Override + public Set getInterfaces() { + return interfaces; + } + + @Override + public MethodReader getMethod(MethodDescriptor method) { + return methods.get(method); + } + + @Override + public Collection getMethods() { + return methods.values(); + } + + @Override + public FieldReader getField(String name) { + return fields.get(name); + } + + @Override + public Collection getFields() { + return fields.values(); + } + + @Override + public String getOwnerName() { + return owner; + } +} diff --git a/core/src/main/java/org/teavm/cache/CachedElement.java b/core/src/main/java/org/teavm/cache/CachedElement.java new file mode 100644 index 000000000..8e2d8bb2e --- /dev/null +++ b/core/src/main/java/org/teavm/cache/CachedElement.java @@ -0,0 +1,54 @@ +/* + * Copyright 2019 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.cache; + +import java.util.EnumSet; +import org.teavm.model.AccessLevel; +import org.teavm.model.AnnotationContainerReader; +import org.teavm.model.ElementModifier; +import org.teavm.model.ElementReader; + +class CachedElement implements ElementReader { + EnumSet modifiers; + CachedAnnotations annotations; + AccessLevel level; + String name; + + @Override + public AccessLevel getLevel() { + return level; + } + + @Override + public EnumSet readModifiers() { + return modifiers.clone(); + } + + @Override + public boolean hasModifier(ElementModifier modifier) { + return modifiers.contains(modifier); + } + + @Override + public String getName() { + return name; + } + + @Override + public AnnotationContainerReader getAnnotations() { + return annotations; + } +} diff --git a/core/src/main/java/org/teavm/cache/CachedField.java b/core/src/main/java/org/teavm/cache/CachedField.java new file mode 100644 index 000000000..c5199470d --- /dev/null +++ b/core/src/main/java/org/teavm/cache/CachedField.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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.cache; + +import org.teavm.model.FieldReader; +import org.teavm.model.FieldReference; +import org.teavm.model.ValueType; + +class CachedField extends CachedMember implements FieldReader { + ValueType type; + Object initialValue; + FieldReference reference; + + @Override + public ValueType getType() { + return type; + } + + @Override + public Object getInitialValue() { + return initialValue; + } + + @Override + public FieldReference getReference() { + return reference; + } +} diff --git a/core/src/main/java/org/teavm/cache/CachedMember.java b/core/src/main/java/org/teavm/cache/CachedMember.java new file mode 100644 index 000000000..d255f6121 --- /dev/null +++ b/core/src/main/java/org/teavm/cache/CachedMember.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019 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.cache; + +import org.teavm.model.MemberReader; + +class CachedMember extends CachedElement implements MemberReader { + String ownerName; + + @Override + public String getOwnerName() { + return ownerName; + } +} diff --git a/core/src/main/java/org/teavm/cache/CachedMethod.java b/core/src/main/java/org/teavm/cache/CachedMethod.java new file mode 100644 index 000000000..91414a2fa --- /dev/null +++ b/core/src/main/java/org/teavm/cache/CachedMethod.java @@ -0,0 +1,97 @@ +/* + * Copyright 2019 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.cache; + +import java.lang.ref.WeakReference; +import java.util.function.Supplier; +import org.teavm.model.AnnotationContainerReader; +import org.teavm.model.AnnotationValue; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; +import org.teavm.model.ProgramReader; +import org.teavm.model.ValueType; + +class CachedMethod extends CachedMember implements MethodReader { + MethodReference reference; + CachedAnnotations[] parameterAnnotations; + AnnotationValue annotationDefault; + WeakReference program; + Supplier programSupplier; + + @Override + public ValueType getResultType() { + return reference.getReturnType(); + } + + @Override + public int parameterCount() { + return reference.parameterCount(); + } + + @Override + public ValueType[] getSignature() { + return reference.getSignature(); + } + + @Override + public ValueType parameterType(int index) { + return reference.parameterType(index); + } + + @Override + public ValueType[] getParameterTypes() { + return reference.getParameterTypes(); + } + + @Override + public AnnotationContainerReader parameterAnnotation(int index) { + return parameterAnnotations[index]; + } + + @Override + public AnnotationContainerReader[] getParameterAnnotations() { + return parameterAnnotations.clone(); + } + + @Override + public MethodDescriptor getDescriptor() { + return reference.getDescriptor(); + } + + @Override + public MethodReference getReference() { + return reference; + } + + @Override + public ProgramReader getProgram() { + if (programSupplier == null) { + return null; + } + ProgramReader program = this.program != null ? this.program.get() : null; + if (program == null) { + program = programSupplier.get(); + this.program = new WeakReference<>(program); + } + return program; + } + + @Override + public AnnotationValue getAnnotationDefault() { + return annotationDefault; + } +} diff --git a/core/src/main/java/org/teavm/cache/ClassIO.java b/core/src/main/java/org/teavm/cache/ClassIO.java index 035baac07..4c618bcbc 100644 --- a/core/src/main/java/org/teavm/cache/ClassIO.java +++ b/core/src/main/java/org/teavm/cache/ClassIO.java @@ -15,43 +15,51 @@ */ package org.teavm.cache; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.teavm.model.AccessLevel; -import org.teavm.model.AnnotationContainer; -import org.teavm.model.AnnotationHolder; +import org.teavm.model.AnnotationContainerReader; import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationValue; -import org.teavm.model.ClassHolder; +import org.teavm.model.ClassReader; import org.teavm.model.ElementModifier; -import org.teavm.model.FieldHolder; +import org.teavm.model.FieldReader; import org.teavm.model.FieldReference; import org.teavm.model.MethodDescriptor; -import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReader; import org.teavm.model.ReferenceCache; import org.teavm.model.ValueType; public class ClassIO { private static AccessLevel[] accessLevels = AccessLevel.values(); private static ElementModifier[] elementModifiers = ElementModifier.values(); + private ReferenceCache referenceCache; private SymbolTable symbolTable; private ProgramIO programIO; public ClassIO(ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable, SymbolTable varTable) { + this.referenceCache = referenceCache; this.symbolTable = symbolTable; programIO = new ProgramIO(referenceCache, symbolTable, fileTable, varTable); } - public void writeClass(OutputStream stream, ClassHolder cls) throws IOException { + public void writeClass(OutputStream stream, ClassReader cls) throws IOException { VarDataOutput output = new VarDataOutput(stream); output.writeUnsigned(cls.getLevel().ordinal()); - output.writeUnsigned(packModifiers(cls.getModifiers())); + output.writeUnsigned(packModifiers(cls.readModifiers())); output.writeUnsigned(cls.getParent() != null ? symbolTable.lookup(cls.getParent()) + 1 : 0); output.writeUnsigned(cls.getOwnerName() != null ? symbolTable.lookup(cls.getOwnerName()) + 1 : 0); output.writeUnsigned(cls.getInterfaces().size()); @@ -60,56 +68,71 @@ public class ClassIO { } writeAnnotations(output, cls.getAnnotations()); output.writeUnsigned(cls.getFields().size()); - for (FieldHolder field : cls.getFields()) { + for (FieldReader field : cls.getFields()) { writeField(output, field); } output.writeUnsigned(cls.getMethods().size()); - for (MethodHolder method : cls.getMethods()) { + for (MethodReader method : cls.getMethods()) { writeMethod(output, method); } } - public ClassHolder readClass(InputStream stream, String name) throws IOException { + public ClassReader readClass(InputStream stream, String name) throws IOException { VarDataInput input = new VarDataInput(stream); - ClassHolder cls = new ClassHolder(name); - cls.setLevel(accessLevels[input.readUnsigned()]); - cls.getModifiers().addAll(unpackModifiers(input.readUnsigned())); + CachedClassReader cls = new CachedClassReader(); + cls.name = name; + cls.level = accessLevels[input.readUnsigned()]; + cls.modifiers = unpackModifiers(input.readUnsigned()); int parentIndex = input.readUnsigned(); - cls.setParent(parentIndex > 0 ? symbolTable.at(parentIndex - 1) : null); + cls.parent = parentIndex > 0 ? referenceCache.getCached(symbolTable.at(parentIndex - 1)) : null; int ownerIndex = input.readUnsigned(); - cls.setOwnerName(ownerIndex > 0 ? symbolTable.at(ownerIndex - 1) : null); + cls.owner = ownerIndex > 0 ? referenceCache.getCached(symbolTable.at(ownerIndex - 1)) : null; int ifaceCount = input.readUnsigned(); + Set interfaces = new LinkedHashSet<>(); for (int i = 0; i < ifaceCount; ++i) { - cls.getInterfaces().add(symbolTable.at(input.readUnsigned())); + interfaces.add(referenceCache.getCached(symbolTable.at(input.readUnsigned()))); } - readAnnotations(input, cls.getAnnotations()); + cls.interfaces = Collections.unmodifiableSet(interfaces); + cls.annotations = readAnnotations(input); + + Map fields = new LinkedHashMap<>(); int fieldCount = input.readUnsigned(); for (int i = 0; i < fieldCount; ++i) { - cls.addField(readField(input)); + CachedField field = readField(name, input); + fields.put(field.name, field); } + cls.fields = fields; + + Map methods = new LinkedHashMap<>(); int methodCount = input.readUnsigned(); for (int i = 0; i < methodCount; ++i) { - cls.addMethod(readMethod(input)); + CachedMethod method = readMethod(cls.name, input); + methods.put(method.reference.getDescriptor(), method); } + cls.methods = methods; + return cls; } - private void writeField(VarDataOutput output, FieldHolder field) throws IOException { + private void writeField(VarDataOutput output, FieldReader field) throws IOException { output.writeUnsigned(symbolTable.lookup(field.getName())); output.writeUnsigned(symbolTable.lookup(field.getType().toString())); output.writeUnsigned(field.getLevel().ordinal()); - output.writeUnsigned(packModifiers(field.getModifiers())); + output.writeUnsigned(packModifiers(field.readModifiers())); writeFieldValue(output, field.getInitialValue()); writeAnnotations(output, field.getAnnotations()); } - private FieldHolder readField(VarDataInput input) throws IOException { - FieldHolder field = new FieldHolder(symbolTable.at(input.readUnsigned())); - field.setType(ValueType.parse(symbolTable.at(input.readUnsigned()))); - field.setLevel(accessLevels[input.readUnsigned()]); - field.getModifiers().addAll(unpackModifiers(input.readUnsigned())); - field.setInitialValue(readFieldValue(input)); - readAnnotations(input, field.getAnnotations()); + private CachedField readField(String className, VarDataInput input) throws IOException { + CachedField field = new CachedField(); + field.name = referenceCache.getCached(symbolTable.at(input.readUnsigned())); + field.type = referenceCache.getCached(ValueType.parse(symbolTable.at(input.readUnsigned()))); + field.level = accessLevels[input.readUnsigned()]; + field.modifiers = unpackModifiers(input.readUnsigned()); + field.initialValue = readFieldValue(input); + field.annotations = readAnnotations(input); + field.ownerName = className; + field.reference = referenceCache.getCached(new FieldReference(className, field.name)); return field; } @@ -154,13 +177,13 @@ public class ClassIO { } } - private void writeMethod(VarDataOutput output, MethodHolder method) throws IOException { + private void writeMethod(VarDataOutput output, MethodReader method) throws IOException { output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString())); output.writeUnsigned(method.getLevel().ordinal()); - output.writeUnsigned(packModifiers(method.getModifiers())); + output.writeUnsigned(packModifiers(method.readModifiers())); writeAnnotations(output, method.getAnnotations()); - for (AnnotationContainer parameterAnnotation : method.getParameterAnnotations()) { + for (AnnotationContainerReader parameterAnnotation : method.getParameterAnnotations()) { writeAnnotations(output, parameterAnnotation); } @@ -173,49 +196,68 @@ public class ClassIO { if (method.getProgram() != null) { output.writeUnsigned(1); - programIO.write(method.getProgram(), output); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + VarDataOutput programOutput = new VarDataOutput(buffer); + programIO.write(method.getProgram(), programOutput); + output.writeBytes(buffer.toByteArray()); } else { output.writeUnsigned(0); } } - private MethodHolder readMethod(VarDataInput input) throws IOException { - MethodHolder method = new MethodHolder(MethodDescriptor.parse(symbolTable.at(input.readUnsigned()))); - method.setLevel(accessLevels[input.readUnsigned()]); - method.getModifiers().addAll(unpackModifiers(input.readUnsigned())); - readAnnotations(input, method.getAnnotations()); + private CachedMethod readMethod(String className, VarDataInput input) throws IOException { + CachedMethod method = new CachedMethod(); + MethodDescriptor descriptor = referenceCache.getCached( + MethodDescriptor.parse(symbolTable.at(input.readUnsigned()))); + method.reference = referenceCache.getCached(className, descriptor); + method.level = accessLevels[input.readUnsigned()]; + method.modifiers = unpackModifiers(input.readUnsigned()); + method.annotations = readAnnotations(input); + method.ownerName = className; + method.name = descriptor.getName(); + method.parameterAnnotations = new CachedAnnotations[descriptor.parameterCount()]; for (int i = 0; i < method.parameterCount(); ++i) { - readAnnotations(input, method.parameterAnnotation(i)); + method.parameterAnnotations[i] = readAnnotations(input); } if (input.readUnsigned() != 0) { - method.setAnnotationDefault(readAnnotationValue(input)); + method.annotationDefault = readAnnotationValue(input); } if (input.readUnsigned() != 0) { - method.setProgram(programIO.read(input)); + byte[] programData = input.readBytes(); + method.programSupplier = () -> { + VarDataInput programInput = new VarDataInput(new ByteArrayInputStream(programData)); + try { + return programIO.read(programInput); + } catch (IOException e) { + throw new IllegalStateException(e); + } + }; } return method; } - private void writeAnnotations(VarDataOutput output, AnnotationContainer annotations) throws IOException { - List annotationList = new ArrayList<>(); - for (AnnotationHolder annot : annotations.all()) { + private void writeAnnotations(VarDataOutput output, AnnotationContainerReader annotations) throws IOException { + List annotationList = new ArrayList<>(); + for (AnnotationReader annot : annotations.all()) { annotationList.add(annot); } output.writeUnsigned(annotationList.size()); - for (AnnotationHolder annot : annotationList) { + for (AnnotationReader annot : annotationList) { writeAnnotation(output, annot); } } - private void readAnnotations(VarDataInput input, AnnotationContainer annotations) throws IOException { + private CachedAnnotations readAnnotations(VarDataInput input) throws IOException { + Map annotations = new HashMap<>(); int annotCount = input.readUnsigned(); for (int i = 0; i < annotCount; ++i) { - AnnotationHolder annot = readAnnotation(input); - annotations.add(annot); + CachedAnnotation annot = readAnnotation(input); + annotations.put(annot.type, annot); } + return new CachedAnnotations(annotations); } private void writeAnnotation(VarDataOutput output, AnnotationReader annotation) throws IOException { @@ -231,14 +273,17 @@ public class ClassIO { } } - private AnnotationHolder readAnnotation(VarDataInput input) throws IOException { - AnnotationHolder annotation = new AnnotationHolder(symbolTable.at(input.readUnsigned())); + private CachedAnnotation readAnnotation(VarDataInput input) throws IOException { + CachedAnnotation annotation = new CachedAnnotation(); + annotation.type = referenceCache.getCached(symbolTable.at(input.readUnsigned())); int valueCount = input.readUnsigned(); + Map fields = new HashMap<>(); for (int i = 0; i < valueCount; ++i) { - String name = symbolTable.at(input.readUnsigned()); + String name = referenceCache.getCached(symbolTable.at(input.readUnsigned())); AnnotationValue value = readAnnotationValue(input); - annotation.getValues().put(name, value); + fields.put(name, value); } + annotation.fields = fields; return annotation; } @@ -300,13 +345,14 @@ public class ClassIO { case AnnotationValue.BYTE: return new AnnotationValue((byte) input.readSigned()); case AnnotationValue.CLASS: - return new AnnotationValue(ValueType.parse(symbolTable.at(input.readUnsigned()))); + return new AnnotationValue(referenceCache.getCached(ValueType.parse( + symbolTable.at(input.readUnsigned())))); case AnnotationValue.DOUBLE: return new AnnotationValue(input.readDouble()); case AnnotationValue.ENUM: { - String className = symbolTable.at(input.readUnsigned()); - String fieldName = symbolTable.at(input.readUnsigned()); - return new AnnotationValue(new FieldReference(className, fieldName)); + String className = referenceCache.getCached(symbolTable.at(input.readUnsigned())); + String fieldName = referenceCache.getCached(symbolTable.at(input.readUnsigned())); + return new AnnotationValue(referenceCache.getCached(new FieldReference(className, fieldName))); } case AnnotationValue.FLOAT: return new AnnotationValue(input.readFloat()); @@ -339,8 +385,8 @@ public class ClassIO { return result; } - private Set unpackModifiers(int packed) { - Set modifiers = EnumSet.noneOf(ElementModifier.class); + private EnumSet unpackModifiers(int packed) { + EnumSet modifiers = EnumSet.noneOf(ElementModifier.class); while (packed != 0) { int n = Integer.numberOfTrailingZeros(packed); packed ^= 1 << n; diff --git a/core/src/main/java/org/teavm/cache/DiskCachedClassHolderSource.java b/core/src/main/java/org/teavm/cache/DiskCachedClassReaderSource.java similarity index 93% rename from core/src/main/java/org/teavm/cache/DiskCachedClassHolderSource.java rename to core/src/main/java/org/teavm/cache/DiskCachedClassReaderSource.java index e69d2e8c6..4fc85aca7 100644 --- a/core/src/main/java/org/teavm/cache/DiskCachedClassHolderSource.java +++ b/core/src/main/java/org/teavm/cache/DiskCachedClassReaderSource.java @@ -28,13 +28,14 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderSource; +import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; import org.teavm.model.MethodReference; import org.teavm.model.ReferenceCache; import org.teavm.parsing.ClassDateProvider; -public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStatus { +public class DiskCachedClassReaderSource implements ClassReaderSource, CacheStatus { private File directory; private ClassHolderSource innerSource; private ClassDateProvider classDateProvider; @@ -42,7 +43,7 @@ public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStat private Set newClasses = new HashSet<>(); private ClassIO classIO; - public DiskCachedClassHolderSource(File directory, ReferenceCache referenceCache, SymbolTable symbolTable, + public DiskCachedClassReaderSource(File directory, ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable, SymbolTable variableTable, ClassHolderSource innerSource, ClassDateProvider classDateProvider) { this.directory = directory; @@ -52,7 +53,7 @@ public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStat } @Override - public ClassHolder get(String name) { + public ClassReader get(String name) { return getItemFromCache(name).cls; } @@ -93,7 +94,7 @@ public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStat } private static class Item { - ClassHolder cls; + ClassReader cls; boolean dirty; } diff --git a/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java b/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java index 21d21bb82..1c108429f 100644 --- a/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java +++ b/core/src/main/java/org/teavm/cache/MemoryCachedClassReaderSource.java @@ -18,14 +18,12 @@ package org.teavm.cache; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.Function; -import org.teavm.model.ClassHolder; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.MethodReference; @@ -33,7 +31,7 @@ import org.teavm.model.ReferenceCache; public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheStatus { private Map cache = new HashMap<>(); - private Function provider; + private Function provider; private ClassIO classIO; private final Set freshClasses = new HashSet<>(); @@ -42,7 +40,7 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt classIO = new ClassIO(referenceCache, symbolTable, fileTable, varTable); } - public void setProvider(Function provider) { + public void setProvider(Function provider) { this.provider = provider; } @@ -56,10 +54,33 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt return isStaleClass(method.getClassName()); } + public void populate(String name) { + getEntry(name); + } + @Override public ClassReader get(String name) { - Entry entry = cache.computeIfAbsent(name, className -> { - ClassHolder cls = provider.apply(name); + Entry entry = getEntry(name); + if (entry.data == null) { + return null; + } + + ClassReader cls = entry.reader; + if (cls == null) { + ByteArrayInputStream input = new ByteArrayInputStream(entry.data); + try { + cls = classIO.readClass(input, name); + } catch (IOException e) { + throw new RuntimeException(e); + } + entry.reader = cls; + } + return cls; + } + + private Entry getEntry(String name) { + return cache.computeIfAbsent(name, className -> { + ClassReader cls = provider.apply(className); Entry en = new Entry(); if (cls != null) { ByteArrayOutputStream output = new ByteArrayOutputStream(); @@ -69,26 +90,9 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt throw new RuntimeException(e); } en.data = output.toByteArray(); - en.reader = new WeakReference<>(cls); } return en; }); - - if (entry.data == null) { - return null; - } - - ClassReader cls = entry.reader.get(); - if (cls == null) { - ByteArrayInputStream input = new ByteArrayInputStream(entry.data); - try { - cls = classIO.readClass(input, name); - } catch (IOException e) { - throw new RuntimeException(e); - } - entry.reader = new WeakReference<>(cls); - } - return cls; } public void commit() { @@ -107,6 +111,6 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt class Entry { byte[] data; - WeakReference reader; + ClassReader reader; } } diff --git a/core/src/main/java/org/teavm/cache/ProgramIO.java b/core/src/main/java/org/teavm/cache/ProgramIO.java index 8ec2c98a5..5a2030b3c 100644 --- a/core/src/main/java/org/teavm/cache/ProgramIO.java +++ b/core/src/main/java/org/teavm/cache/ProgramIO.java @@ -18,23 +18,29 @@ package org.teavm.cache; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Objects; +import java.util.List; import org.teavm.model.BasicBlock; +import org.teavm.model.BasicBlockReader; import org.teavm.model.FieldReference; import org.teavm.model.Incoming; +import org.teavm.model.IncomingReader; import org.teavm.model.Instruction; import org.teavm.model.InvokeDynamicInstruction; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodHandle; import org.teavm.model.MethodReference; import org.teavm.model.Phi; +import org.teavm.model.PhiReader; import org.teavm.model.Program; +import org.teavm.model.ProgramReader; import org.teavm.model.ReferenceCache; import org.teavm.model.RuntimeConstant; import org.teavm.model.TextLocation; import org.teavm.model.TryCatchBlock; +import org.teavm.model.TryCatchBlockReader; import org.teavm.model.ValueType; import org.teavm.model.Variable; +import org.teavm.model.VariableReader; import org.teavm.model.instructions.ArrayElementType; import org.teavm.model.instructions.ArrayLengthInstruction; import org.teavm.model.instructions.AssignInstruction; @@ -60,7 +66,7 @@ import org.teavm.model.instructions.FloatConstantInstruction; import org.teavm.model.instructions.GetElementInstruction; import org.teavm.model.instructions.GetFieldInstruction; import org.teavm.model.instructions.InitClassInstruction; -import org.teavm.model.instructions.InstructionVisitor; +import org.teavm.model.instructions.InstructionReader; import org.teavm.model.instructions.IntegerConstantInstruction; import org.teavm.model.instructions.IntegerSubtype; import org.teavm.model.instructions.InvocationType; @@ -80,6 +86,7 @@ import org.teavm.model.instructions.RaiseInstruction; import org.teavm.model.instructions.StringConstantInstruction; import org.teavm.model.instructions.SwitchInstruction; import org.teavm.model.instructions.SwitchTableEntry; +import org.teavm.model.instructions.SwitchTableEntryReader; import org.teavm.model.instructions.UnwrapArrayInstruction; public class ProgramIO { @@ -103,62 +110,42 @@ public class ProgramIO { this.variableTable = variableTable; } - public void write(Program program, OutputStream output) throws IOException { + public void write(ProgramReader program, OutputStream output) throws IOException { write(program, new VarDataOutput(output)); } - public void write(Program program, VarDataOutput data) throws IOException { + public void write(ProgramReader program, VarDataOutput data) throws IOException { data.writeUnsigned(program.variableCount()); data.writeUnsigned(program.basicBlockCount()); for (int i = 0; i < program.variableCount(); ++i) { - Variable var = program.variableAt(i); + VariableReader var = program.variableAt(i); data.writeUnsigned(var.getRegister()); data.writeUnsigned(var.getDebugName() != null ? variableTable.lookup(var.getDebugName()) + 1 : 0); } for (int i = 0; i < program.basicBlockCount(); ++i) { - BasicBlock basicBlock = program.basicBlockAt(i); + BasicBlockReader basicBlock = program.basicBlockAt(i); data.writeUnsigned(basicBlock.getExceptionVariable() != null ? basicBlock.getExceptionVariable().getIndex() + 1 : 0); - data.writeUnsigned(basicBlock.getPhis().size()); - data.writeUnsigned(basicBlock.getTryCatchBlocks().size()); - for (Phi phi : basicBlock.getPhis()) { + data.writeUnsigned(basicBlock.readPhis().size()); + data.writeUnsigned(basicBlock.readTryCatchBlocks().size()); + for (PhiReader phi : basicBlock.readPhis()) { data.writeUnsigned(phi.getReceiver().getIndex()); - data.writeUnsigned(phi.getIncomings().size()); - for (Incoming incoming : phi.getIncomings()) { + data.writeUnsigned(phi.readIncomings().size()); + for (IncomingReader incoming : phi.readIncomings()) { data.writeUnsigned(incoming.getSource().getIndex()); data.writeUnsigned(incoming.getValue().getIndex()); } } - for (TryCatchBlock tryCatch : basicBlock.getTryCatchBlocks()) { + for (TryCatchBlockReader tryCatch : basicBlock.readTryCatchBlocks()) { data.writeUnsigned(tryCatch.getExceptionType() != null ? symbolTable.lookup( tryCatch.getExceptionType()) + 1 : 0); data.writeUnsigned(tryCatch.getHandler().getIndex()); } - TextLocation location = null; InstructionWriter insnWriter = new InstructionWriter(data); - for (Instruction insn : basicBlock) { - try { - if (!Objects.equals(location, insn.getLocation())) { - TextLocation newLocation = insn.getLocation(); - if (newLocation == null || newLocation.getFileName() == null || newLocation.getLine() < 0) { - data.writeUnsigned(1); - location = null; - } else { - if (location != null && location.getFileName().equals(newLocation.getFileName())) { - data.writeUnsigned(127); - data.writeSigned(newLocation.getLine() - location.getLine()); - } else { - data.writeUnsigned(2); - data.writeUnsigned(fileTable.lookup(newLocation.getFileName())); - data.writeUnsigned(newLocation.getLine()); - } - location = newLocation; - } - } - insn.acceptVisitor(insnWriter); - } catch (IOExceptionWrapper e) { - throw (IOException) e.getCause(); - } + try { + basicBlock.readAllInstructions(insnWriter); + } catch (IOExceptionWrapper e) { + throw (IOException) e.getCause(); } data.writeUnsigned(0); } @@ -246,15 +233,38 @@ public class ProgramIO { return program; } - private class InstructionWriter implements InstructionVisitor { + private class InstructionWriter implements InstructionReader { private VarDataOutput output; + TextLocation location; InstructionWriter(VarDataOutput output) { this.output = output; } @Override - public void visit(EmptyInstruction insn) { + public void location(TextLocation newLocation) { + try { + if (newLocation == null || newLocation.getFileName() == null || newLocation.getLine() < 0) { + output.writeUnsigned(1); + location = null; + } else { + if (location != null && location.getFileName().equals(newLocation.getFileName())) { + output.writeUnsigned(127); + output.writeSigned(newLocation.getLine() - location.getLine()); + } else { + output.writeUnsigned(2); + output.writeUnsigned(fileTable.lookup(newLocation.getFileName())); + output.writeUnsigned(newLocation.getLine()); + } + location = newLocation; + } + } catch (IOException e) { + throw new IOExceptionWrapper(e); + } + } + + @Override + public void nop() { try { output.writeUnsigned(3); } catch (IOException e) { @@ -263,196 +273,202 @@ public class ProgramIO { } @Override - public void visit(ClassConstantInstruction insn) { + public void classConstant(VariableReader receiver, ValueType cst) { try { output.writeUnsigned(4); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(symbolTable.lookup(insn.getConstant().toString())); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(symbolTable.lookup(cst.toString())); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(NullConstantInstruction insn) { + public void nullConstant(VariableReader receiver) { try { output.writeUnsigned(5); - output.writeUnsigned(insn.getReceiver().getIndex()); + output.writeUnsigned(receiver.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(IntegerConstantInstruction insn) { + public void integerConstant(VariableReader receiver, int cst) { try { output.writeUnsigned(6); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeSigned(insn.getConstant()); + output.writeUnsigned(receiver.getIndex()); + output.writeSigned(cst); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(LongConstantInstruction insn) { + public void longConstant(VariableReader receiver, long cst) { try { output.writeUnsigned(7); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeSigned(insn.getConstant()); + output.writeUnsigned(receiver.getIndex()); + output.writeSigned(cst); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(FloatConstantInstruction insn) { + public void floatConstant(VariableReader receiver, float cst) { try { output.writeUnsigned(8); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeFloat(insn.getConstant()); + output.writeUnsigned(receiver.getIndex()); + output.writeFloat(cst); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(DoubleConstantInstruction insn) { + public void doubleConstant(VariableReader receiver, double cst) { try { output.writeUnsigned(9); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeDouble(insn.getConstant()); + output.writeUnsigned(receiver.getIndex()); + output.writeDouble(cst); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(StringConstantInstruction insn) { + public void stringConstant(VariableReader receiver, String cst) { try { output.writeUnsigned(10); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.write(insn.getConstant()); + output.writeUnsigned(receiver.getIndex()); + output.write(cst); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(BinaryInstruction insn) { + public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second, + NumericOperandType type) { try { - output.writeUnsigned(11 + insn.getOperation().ordinal()); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getOperandType().ordinal()); - output.writeUnsigned(insn.getFirstOperand().getIndex()); - output.writeUnsigned(insn.getSecondOperand().getIndex()); + output.writeUnsigned(11 + op.ordinal()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(type.ordinal()); + output.writeUnsigned(first.getIndex()); + output.writeUnsigned(second.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(NegateInstruction insn) { + public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) { try { output.writeUnsigned(23); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getOperandType().ordinal()); - output.writeUnsigned(insn.getOperand().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(type.ordinal()); + output.writeUnsigned(operand.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(AssignInstruction insn) { + public void assign(VariableReader receiver, VariableReader assignee) { try { output.writeUnsigned(24); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getAssignee().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(assignee.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(CastInstruction insn) { + public void cast(VariableReader receiver, VariableReader value, ValueType targetType) { try { output.writeUnsigned(25); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(symbolTable.lookup(insn.getTargetType().toString())); - output.writeUnsigned(insn.getValue().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(symbolTable.lookup(targetType.toString())); + output.writeUnsigned(value.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(CastNumberInstruction insn) { + public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType, + NumericOperandType targetType) { try { output.writeUnsigned(26); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getSourceType().ordinal() | (insn.getTargetType().ordinal() << 2)); - output.writeUnsigned(insn.getValue().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(sourceType.ordinal() | (targetType.ordinal() << 2)); + output.writeUnsigned(value.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(CastIntegerInstruction insn) { + public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type, + CastIntegerDirection direction) { try { output.writeUnsigned(27); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getDirection().ordinal() | (insn.getTargetType().ordinal() << 1)); - output.writeUnsigned(insn.getValue().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(direction.ordinal() | (type.ordinal() << 1)); + output.writeUnsigned(value.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(BranchingInstruction insn) { + public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent, + BasicBlockReader alternative) { try { - output.writeUnsigned(28 + insn.getCondition().ordinal()); - output.writeUnsigned(insn.getOperand().getIndex()); - output.writeUnsigned(insn.getConsequent().getIndex()); - output.writeUnsigned(insn.getAlternative().getIndex()); + output.writeUnsigned(28 + cond.ordinal()); + output.writeUnsigned(operand.getIndex()); + output.writeUnsigned(consequent.getIndex()); + output.writeUnsigned(alternative.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(BinaryBranchingInstruction insn) { + public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second, + BasicBlockReader consequent, BasicBlockReader alternative) { try { - output.writeUnsigned(36 + insn.getCondition().ordinal()); - output.writeUnsigned(insn.getFirstOperand().getIndex()); - output.writeUnsigned(insn.getSecondOperand().getIndex()); - output.writeUnsigned(insn.getConsequent().getIndex()); - output.writeUnsigned(insn.getAlternative().getIndex()); + output.writeUnsigned(36 + cond.ordinal()); + output.writeUnsigned(first.getIndex()); + output.writeUnsigned(second.getIndex()); + output.writeUnsigned(consequent.getIndex()); + output.writeUnsigned(alternative.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(JumpInstruction insn) { + public void jump(BasicBlockReader target) { try { output.writeUnsigned(40); - output.writeUnsigned(insn.getTarget().getIndex()); + output.writeUnsigned(target.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(SwitchInstruction insn) { + public void choose(VariableReader condition, List table, + BasicBlockReader defaultTarget) { try { output.writeUnsigned(41); - output.writeUnsigned(insn.getCondition().getIndex()); - output.writeUnsigned(insn.getDefaultTarget().getIndex()); - output.writeUnsigned(insn.getEntries().size()); - for (SwitchTableEntry entry : insn.getEntries()) { + output.writeUnsigned(condition.getIndex()); + output.writeUnsigned(defaultTarget.getIndex()); + output.writeUnsigned(table.size()); + for (SwitchTableEntryReader entry : table) { output.writeSigned(entry.getCondition()); output.writeUnsigned(entry.getTarget().getIndex()); } @@ -462,11 +478,11 @@ public class ProgramIO { } @Override - public void visit(ExitInstruction insn) { + public void exit(VariableReader valueToReturn) { try { - if (insn.getValueToReturn() != null) { + if (valueToReturn != null) { output.writeUnsigned(42); - output.writeUnsigned(insn.getValueToReturn().getIndex()); + output.writeUnsigned(valueToReturn.getIndex()); } else { output.writeUnsigned(43); } @@ -476,46 +492,36 @@ public class ProgramIO { } @Override - public void visit(RaiseInstruction insn) { + public void raise(VariableReader exception) { try { output.writeUnsigned(44); - output.writeUnsigned(insn.getException().getIndex()); + output.writeUnsigned(exception.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(ConstructArrayInstruction insn) { + public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) { try { output.writeUnsigned(45); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(symbolTable.lookup(insn.getItemType().toString())); - output.writeUnsigned(insn.getSize().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(symbolTable.lookup(itemType.toString())); + output.writeUnsigned(size.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(ConstructInstruction insn) { - try { - output.writeUnsigned(46); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(symbolTable.lookup(insn.getType())); - } catch (IOException e) { - throw new IOExceptionWrapper(e); - } - } - - @Override - public void visit(ConstructMultiArrayInstruction insn) { + public void createArray(VariableReader receiver, ValueType itemType, + List dimensions) { try { output.writeUnsigned(47); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(symbolTable.lookup(insn.getItemType().toString())); - output.writeUnsigned(insn.getDimensions().size()); - for (Variable dimension : insn.getDimensions()) { + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(symbolTable.lookup(itemType.toString())); + output.writeUnsigned(dimensions.size()); + for (VariableReader dimension : dimensions) { output.writeUnsigned(dimension.getIndex()); } } catch (IOException e) { @@ -524,113 +530,128 @@ public class ProgramIO { } @Override - public void visit(GetFieldInstruction insn) { + public void create(VariableReader receiver, String type) { try { - output.writeUnsigned(insn.getInstance() != null ? 48 : 49); - output.writeUnsigned(insn.getReceiver().getIndex()); - if (insn.getInstance() != null) { - output.writeUnsigned(insn.getInstance().getIndex()); - } - output.writeUnsigned(symbolTable.lookup(insn.getField().getClassName())); - output.writeUnsigned(symbolTable.lookup(insn.getField().getFieldName())); - output.writeUnsigned(symbolTable.lookup(insn.getFieldType().toString())); + output.writeUnsigned(46); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(symbolTable.lookup(type)); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(PutFieldInstruction insn) { + public void getField(VariableReader receiver, VariableReader instance, FieldReference field, + ValueType fieldType) { try { - output.writeUnsigned(insn.getInstance() != null ? 50 : 51); - if (insn.getInstance() != null) { - output.writeUnsigned(insn.getInstance().getIndex()); + output.writeUnsigned(instance != null ? 48 : 49); + output.writeUnsigned(receiver.getIndex()); + if (instance != null) { + output.writeUnsigned(instance.getIndex()); } - output.writeUnsigned(symbolTable.lookup(insn.getField().getClassName())); - output.writeUnsigned(symbolTable.lookup(insn.getField().getFieldName())); - output.writeUnsigned(symbolTable.lookup(insn.getFieldType().toString())); - output.writeUnsigned(insn.getValue().getIndex()); + output.writeUnsigned(symbolTable.lookup(field.getClassName())); + output.writeUnsigned(symbolTable.lookup(field.getFieldName())); + output.writeUnsigned(symbolTable.lookup(fieldType.toString())); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(ArrayLengthInstruction insn) { + public void putField(VariableReader instance, FieldReference field, VariableReader value, ValueType fieldType) { + try { + output.writeUnsigned(instance != null ? 50 : 51); + if (instance != null) { + output.writeUnsigned(instance.getIndex()); + } + output.writeUnsigned(symbolTable.lookup(field.getClassName())); + output.writeUnsigned(symbolTable.lookup(field.getFieldName())); + output.writeUnsigned(symbolTable.lookup(fieldType.toString())); + output.writeUnsigned(value.getIndex()); + } catch (IOException e) { + throw new IOExceptionWrapper(e); + } + } + + @Override + public void arrayLength(VariableReader receiver, VariableReader array) { try { output.writeUnsigned(52); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getArray().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(array.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(CloneArrayInstruction insn) { + public void cloneArray(VariableReader receiver, VariableReader array) { try { output.writeUnsigned(53); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getArray().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(array.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(UnwrapArrayInstruction insn) { + public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) { try { - output.writeUnsigned(54 + insn.getElementType().ordinal()); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getArray().getIndex()); + output.writeUnsigned(54 + elementType.ordinal()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(array.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(GetElementInstruction insn) { + public void getElement(VariableReader receiver, VariableReader array, VariableReader index, + ArrayElementType elementType) { try { - output.writeUnsigned(62 + insn.getType().ordinal()); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getArray().getIndex()); - output.writeUnsigned(insn.getIndex().getIndex()); + output.writeUnsigned(62 + elementType.ordinal()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(array.getIndex()); + output.writeUnsigned(index.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(PutElementInstruction insn) { + public void putElement(VariableReader array, VariableReader index, VariableReader value, + ArrayElementType elementType) { try { - output.writeUnsigned(70 + insn.getType().ordinal()); - output.writeUnsigned(insn.getArray().getIndex()); - output.writeUnsigned(insn.getIndex().getIndex()); - output.writeUnsigned(insn.getValue().getIndex()); + output.writeUnsigned(70 + elementType.ordinal()); + output.writeUnsigned(array.getIndex()); + output.writeUnsigned(index.getIndex()); + output.writeUnsigned(value.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(InvokeInstruction insn) { + public void invoke(VariableReader receiver, VariableReader instance, MethodReference method, + List arguments, InvocationType type) { try { - switch (insn.getType()) { + switch (type) { case SPECIAL: - output.writeUnsigned(insn.getInstance() == null ? 78 : 79); + output.writeUnsigned(instance == null ? 78 : 79); break; case VIRTUAL: output.writeUnsigned(80); break; } - output.writeUnsigned(insn.getReceiver() != null ? insn.getReceiver().getIndex() + 1 : 0); - if (insn.getInstance() != null) { - output.writeUnsigned(insn.getInstance().getIndex()); + output.writeUnsigned(receiver != null ? receiver.getIndex() + 1 : 0); + if (instance != null) { + output.writeUnsigned(instance.getIndex()); } - output.writeUnsigned(symbolTable.lookup(insn.getMethod().getClassName())); - output.writeUnsigned(symbolTable.lookup(insn.getMethod().getDescriptor().toString())); - for (int i = 0; i < insn.getArguments().size(); ++i) { - output.writeUnsigned(insn.getArguments().get(i).getIndex()); + output.writeUnsigned(symbolTable.lookup(method.getClassName())); + output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString())); + for (int i = 0; i < arguments.size(); ++i) { + output.writeUnsigned(arguments.get(i).getIndex()); } } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -638,19 +659,21 @@ public class ProgramIO { } @Override - public void visit(InvokeDynamicInstruction insn) { + public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method, + List arguments, MethodHandle bootstrapMethod, + List bootstrapArguments) { try { output.writeUnsigned(81); - output.writeUnsigned(insn.getReceiver() != null ? insn.getReceiver().getIndex() + 1 : 0); - output.writeUnsigned(insn.getInstance() != null ? insn.getInstance().getIndex() + 1 : 0); - output.writeUnsigned(symbolTable.lookup(insn.getMethod().toString())); - for (int i = 0; i < insn.getArguments().size(); ++i) { - output.writeUnsigned(insn.getArguments().get(i).getIndex()); + output.writeUnsigned(receiver != null ? receiver.getIndex() + 1 : 0); + output.writeUnsigned(instance != null ? instance.getIndex() + 1 : 0); + output.writeUnsigned(symbolTable.lookup(method.toString())); + for (int i = 0; i < arguments.size(); ++i) { + output.writeUnsigned(arguments.get(i).getIndex()); } - write(insn.getBootstrapMethod()); - output.writeUnsigned(insn.getBootstrapArguments().size()); - for (int i = 0; i < insn.getBootstrapArguments().size(); ++i) { - write(insn.getBootstrapArguments().get(i)); + write(bootstrapMethod); + output.writeUnsigned(bootstrapArguments.size()); + for (int i = 0; i < bootstrapArguments.size(); ++i) { + write(bootstrapArguments.get(i)); } } catch (IOException e) { throw new IOExceptionWrapper(e); @@ -658,53 +681,53 @@ public class ProgramIO { } @Override - public void visit(IsInstanceInstruction insn) { + public void isInstance(VariableReader receiver, VariableReader value, ValueType type) { try { output.writeUnsigned(82); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(symbolTable.lookup(insn.getType().toString())); - output.writeUnsigned(insn.getValue().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(symbolTable.lookup(type.toString())); + output.writeUnsigned(value.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(InitClassInstruction insn) { + public void initClass(String className) { try { output.writeUnsigned(83); - output.writeUnsigned(symbolTable.lookup(insn.getClassName())); + output.writeUnsigned(symbolTable.lookup(className)); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(NullCheckInstruction insn) { + public void nullCheck(VariableReader receiver, VariableReader value) { try { output.writeUnsigned(84); - output.writeUnsigned(insn.getReceiver().getIndex()); - output.writeUnsigned(insn.getValue().getIndex()); + output.writeUnsigned(receiver.getIndex()); + output.writeUnsigned(value.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(MonitorEnterInstruction insn) { + public void monitorEnter(VariableReader objectRef) { try { output.writeUnsigned(85); - output.writeUnsigned(insn.getObjectRef().getIndex()); + output.writeUnsigned(objectRef.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } } @Override - public void visit(MonitorExitInstruction insn) { + public void monitorExit(VariableReader objectRef) { try { output.writeUnsigned(86); - output.writeUnsigned(insn.getObjectRef().getIndex()); + output.writeUnsigned(objectRef.getIndex()); } catch (IOException e) { throw new IOExceptionWrapper(e); } diff --git a/core/src/main/java/org/teavm/cache/VarDataInput.java b/core/src/main/java/org/teavm/cache/VarDataInput.java index 2eac22206..24806ed0e 100644 --- a/core/src/main/java/org/teavm/cache/VarDataInput.java +++ b/core/src/main/java/org/teavm/cache/VarDataInput.java @@ -124,4 +124,24 @@ public class VarDataInput { } return new String(chars); } + + public byte[] readBytes() throws IOException { + byte[] buf = new byte[readUnsigned()]; + int off = 0; + while (true) { + int toRead = buf.length - off; + if (toRead == 0) { + break; + } + int read = input.read(buf, off, toRead); + if (read < 0) { + throw new IllegalStateException(); + } + if (read == toRead) { + break; + } + off += read; + } + return buf; + } } diff --git a/core/src/main/java/org/teavm/cache/VarDataOutput.java b/core/src/main/java/org/teavm/cache/VarDataOutput.java index 9d9e3da1d..923fc3bcd 100644 --- a/core/src/main/java/org/teavm/cache/VarDataOutput.java +++ b/core/src/main/java/org/teavm/cache/VarDataOutput.java @@ -97,6 +97,11 @@ public class VarDataOutput implements Closeable { } } + public void writeBytes(byte[] data) throws IOException { + writeUnsigned(data.length); + output.write(data); + } + @Override public void close() throws IOException { output.close(); diff --git a/core/src/main/java/org/teavm/dependency/ClassDependency.java b/core/src/main/java/org/teavm/dependency/ClassDependency.java index 0bc91cfda..162c76dd0 100644 --- a/core/src/main/java/org/teavm/dependency/ClassDependency.java +++ b/core/src/main/java/org/teavm/dependency/ClassDependency.java @@ -22,6 +22,7 @@ public class ClassDependency implements ClassDependencyInfo { private DependencyAnalyzer analyzer; private String className; private ClassReader classReader; + boolean present; boolean activated; ClassDependency(DependencyAnalyzer analyzer, String className, ClassReader classReader) { @@ -37,7 +38,7 @@ public class ClassDependency implements ClassDependencyInfo { @Override public boolean isMissing() { - return classReader == null; + return classReader == null && !present; } public ClassReader getClassReader() { @@ -49,4 +50,11 @@ public class ClassDependency implements ClassDependencyInfo { analyzer.initClass(this, location); } } + + void cleanup() { + if (classReader != null) { + present = true; + classReader = null; + } + } } diff --git a/core/src/main/java/org/teavm/dependency/ClassSourcePacker.java b/core/src/main/java/org/teavm/dependency/ClassSourcePacker.java new file mode 100644 index 000000000..f95bc677b --- /dev/null +++ b/core/src/main/java/org/teavm/dependency/ClassSourcePacker.java @@ -0,0 +1,23 @@ +/* + * Copyright 2019 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.dependency; + +import java.util.Collection; +import org.teavm.model.ClassReaderSource; + +public interface ClassSourcePacker { + ClassReaderSource pack(ClassReaderSource classSource, Collection classNames); +} diff --git a/core/src/main/java/org/teavm/dependency/DependencyAgent.java b/core/src/main/java/org/teavm/dependency/DependencyAgent.java index 4c36eabbd..294d69b04 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAgent.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAgent.java @@ -80,7 +80,7 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository { @Override public ClassReaderSource getClassSource() { - return analyzer.getClassSource(); + return analyzer.agentClassSource; } @Override diff --git a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java index 7f42614ab..2ed3d5fe3 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Deque; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -82,8 +83,10 @@ public abstract class DependencyAnalyzer implements DependencyInfo { private int classNameSuffix; private ClassReaderSource unprocessedClassSource; private DependencyClassSource classSource; + ClassReaderSource agentClassSource; private ClassLoader classLoader; private Map>> methodReaderCache = new HashMap<>(1000, 0.5f); + private Map implementationCache = new HashMap<>(); private Function fieldReaderCache; private Map> methodCache = new HashMap<>(); private Set reachedMethods = new LinkedHashSet<>(); @@ -111,6 +114,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo { IncrementalCache incrementalCache = new IncrementalCache(); boolean asyncSupported; private ReferenceCache referenceCache; + private Set generatedClassNames = new HashSet<>(); DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, Diagnostics diagnostics, ReferenceCache referenceCache) { @@ -118,6 +122,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo { this.diagnostics = diagnostics; this.referenceCache = referenceCache; this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache); + agentClassSource = this.classSource; classHierarchy = new ClassHierarchy(this.classSource); this.classLoader = classLoader; this.services = services; @@ -206,11 +211,11 @@ public abstract class DependencyAnalyzer implements DependencyInfo { @Override public ClassReaderSource getClassSource() { - return classSource; + return classSource != null ? classSource : agentClassSource; } public boolean isSynthesizedClass(String className) { - return classSource.isGeneratedClass(className); + return classSource != null ? classSource.isGeneratedClass(className) : generatedClassNames.contains(className); } public ClassHierarchy getClassHierarchy() { @@ -608,8 +613,10 @@ public abstract class DependencyAnalyzer implements DependencyInfo { @Override public MethodDependency getMethodImplementation(MethodReference methodRef) { - MethodReader method = getMethodHolder(methodRef.getClassName(), methodRef.getDescriptor()); - return method != null ? getMethod(method.getReference()) : null; + return implementationCache.computeIfAbsent(methodRef, m -> { + MethodReader resolved = agentClassSource.resolveImplementation(m); + return resolved != null ? getMethod(resolved.getReference()) : null; + }); } private MethodHolder getMethodHolder(String className, MethodDescriptor descriptor) { @@ -726,7 +733,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo { System.out.println("Total domains: " + domainCount); } - public void cleanup() { + public void cleanup(ClassSourcePacker classSourcePacker) { for (DependencyNode node : allNodes) { node.followers = null; node.transitions = null; @@ -744,6 +751,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo { for (MethodDependency methodDependency : map.values()) { methodDependency.locationListeners = null; methodDependency.locations = null; + methodDependency.cleanup(); } } @@ -752,15 +760,30 @@ public abstract class DependencyAnalyzer implements DependencyInfo { if (field != null) { field.locationListeners = null; field.locations = null; + field.cleanup(); } } + for (String className : classCache.getCachedPreimages()) { + ClassDependency cls = classCache.getKnown(className); + cls.cleanup(); + } + allNodes.clear(); classSource.cleanup(); agent.cleanup(); listeners.clear(); unprocessedClassSource = null; classSource.innerHierarchy = null; + + agentClassSource = classSourcePacker.pack(classSource, classSource.cache.keySet()); + if (classSource != agentClassSource) { + classHierarchy = new ClassHierarchy(agentClassSource); + generatedClassNames.addAll(classSource.getGeneratedClassNames()); + } + classSource = null; + methodReaderCache = null; + fieldReaderCache = null; } public void cleanupTypes() { diff --git a/core/src/main/java/org/teavm/dependency/DependencyClassSource.java b/core/src/main/java/org/teavm/dependency/DependencyClassSource.java index 55bde6a54..2ed132213 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyClassSource.java +++ b/core/src/main/java/org/teavm/dependency/DependencyClassSource.java @@ -41,7 +41,7 @@ class DependencyClassSource implements ClassHolderSource { private IncrementalDependencyRegistration dependencyRegistration; private Map generatedClasses = new LinkedHashMap<>(); private List transformers = new ArrayList<>(); - private Map> cache = new LinkedHashMap<>(1000, 0.5f); + Map> cache = new LinkedHashMap<>(1000, 0.5f); DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics, IncrementalDependencyRegistration dependencyRegistration) { @@ -93,6 +93,10 @@ class DependencyClassSource implements ClassHolderSource { return generatedClasses.get(name); } + Collection getGeneratedClassNames() { + return generatedClasses.keySet(); + } + public Collection getGeneratedClasses() { return generatedClasses.values(); } diff --git a/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java index 38b051d40..00f5f910e 100644 --- a/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/FastDependencyAnalyzer.java @@ -191,9 +191,9 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer { } @Override - public void cleanup() { + public void cleanup(ClassSourcePacker classSourcePacker) { virtualCallConsumers.clear(); subtypeNodes.clear(); - super.cleanup(); + super.cleanup(classSourcePacker); } } diff --git a/core/src/main/java/org/teavm/dependency/FieldDependency.java b/core/src/main/java/org/teavm/dependency/FieldDependency.java index 27ea3c7ad..5b525c8dd 100644 --- a/core/src/main/java/org/teavm/dependency/FieldDependency.java +++ b/core/src/main/java/org/teavm/dependency/FieldDependency.java @@ -26,6 +26,7 @@ import org.teavm.model.FieldReference; public class FieldDependency implements FieldDependencyInfo { DependencyNode value; private FieldReader field; + private boolean present; private FieldReference reference; List locationListeners; Set locations; @@ -53,7 +54,7 @@ public class FieldDependency implements FieldDependencyInfo { @Override public boolean isMissing() { - return field == null; + return field == null && !present; } public FieldDependency addLocation(CallLocation location) { @@ -83,4 +84,11 @@ public class FieldDependency implements FieldDependencyInfo { } } } + + void cleanup() { + if (field != null) { + field = null; + present = true; + } + } } diff --git a/core/src/main/java/org/teavm/dependency/MethodDependency.java b/core/src/main/java/org/teavm/dependency/MethodDependency.java index a32b0311f..19f18a985 100644 --- a/core/src/main/java/org/teavm/dependency/MethodDependency.java +++ b/core/src/main/java/org/teavm/dependency/MethodDependency.java @@ -32,6 +32,7 @@ public class MethodDependency implements MethodDependencyInfo { DependencyNode resultNode; DependencyNode thrown; MethodHolder method; + boolean present; private MethodReference reference; boolean used; boolean external; @@ -101,7 +102,7 @@ public class MethodDependency implements MethodDependencyInfo { @Override public boolean isMissing() { - return method == null; + return method == null && !present; } @Override @@ -179,4 +180,12 @@ public class MethodDependency implements MethodDependencyInfo { public boolean isCalled() { return external; } + + + void cleanup() { + if (method != null) { + present = true; + method = null; + } + } } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 9bbb35648..78b831e61 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -40,6 +40,7 @@ import org.teavm.cache.EmptyProgramCache; import org.teavm.cache.ProgramDependencyExtractor; import org.teavm.common.ServiceRepository; import org.teavm.dependency.BootstrapMethodSubstitutor; +import org.teavm.dependency.ClassSourcePacker; import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyListener; @@ -152,10 +153,12 @@ public class TeaVM implements TeaVMHost, ServiceRepository { private int compileProgressReportLimit; private int compileProgressLimit; private int compileProgressValue; + private ClassSourcePacker classSourcePacker; TeaVM(TeaVMBuilder builder) { target = builder.target; classLoader = builder.classLoader; + classSourcePacker = builder.classSourcePacker; dependencyAnalyzer = builder.dependencyAnalyzerFactory.create(builder.classSource, classLoader, this, diagnostics, builder.referenceCache); progressListener = new TeaVMProgressListener() { @@ -348,8 +351,6 @@ public class TeaVM implements TeaVMHost, ServiceRepository { * @param outputName name of output file within buildTarget. Should not be null. */ public void build(BuildTarget buildTarget, String outputName) { - target.setController(targetController); - // Check dependencies reportPhase(TeaVMPhase.DEPENDENCY_ANALYSIS, lastKnownClasses > 0 ? lastKnownClasses : 1); if (wasCancelled()) { @@ -368,11 +369,12 @@ public class TeaVM implements TeaVMHost, ServiceRepository { return; } + dependencyAnalyzer.setInterruptor(null); + dependencyAnalyzer.cleanup(classSourcePacker); cacheStatus = new AnnotationAwareCacheStatus(rawCacheStatus, dependencyAnalyzer.getIncrementalDependencies(), dependencyAnalyzer.getClassSource()); cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass); - dependencyAnalyzer.setInterruptor(null); - dependencyAnalyzer.cleanup(); + target.setController(targetController); if (wasCancelled()) { return; diff --git a/core/src/main/java/org/teavm/vm/TeaVMBuilder.java b/core/src/main/java/org/teavm/vm/TeaVMBuilder.java index e2ef88002..ad54d4a1f 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMBuilder.java +++ b/core/src/main/java/org/teavm/vm/TeaVMBuilder.java @@ -15,6 +15,7 @@ */ package org.teavm.vm; +import org.teavm.dependency.ClassSourcePacker; import org.teavm.dependency.DependencyAnalyzerFactory; import org.teavm.dependency.PreciseDependencyAnalyzer; import org.teavm.interop.PlatformMarker; @@ -28,6 +29,7 @@ public class TeaVMBuilder { ClassLoader classLoader; ReferenceCache referenceCache = new ReferenceCache(); DependencyAnalyzerFactory dependencyAnalyzerFactory = PreciseDependencyAnalyzer::new; + ClassSourcePacker classSourcePacker = (src, names) -> src; public TeaVMBuilder(TeaVMTarget target) { this.target = target; @@ -67,6 +69,11 @@ public class TeaVMBuilder { return this; } + public TeaVMBuilder setClassSourcePacker(ClassSourcePacker classSourcePacker) { + this.classSourcePacker = classSourcePacker; + return this; + } + public TeaVM build() { return new TeaVM(this); } diff --git a/tests/src/test/java/org/teavm/incremental/IncrementalTest.java b/tests/src/test/java/org/teavm/incremental/IncrementalTest.java index 5704a0d4b..66ae0ca7e 100644 --- a/tests/src/test/java/org/teavm/incremental/IncrementalTest.java +++ b/tests/src/test/java/org/teavm/incremental/IncrementalTest.java @@ -292,7 +292,7 @@ public class IncrementalTest { public boolean isStaleClass(String className) { ClassReader cls = underlying.get(className); if (cls == null) { - return true; + return false; } return cls.getAnnotations().get(Update.class.getName()) != null; diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java index c809fff65..56d4ac881 100644 --- a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -38,7 +38,7 @@ import org.teavm.backend.wasm.WasmTarget; import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.cache.AlwaysStaleCacheStatus; import org.teavm.cache.CacheStatus; -import org.teavm.cache.DiskCachedClassHolderSource; +import org.teavm.cache.DiskCachedClassReaderSource; import org.teavm.cache.DiskMethodNodeCache; import org.teavm.cache.DiskProgramCache; import org.teavm.cache.EmptyProgramCache; @@ -83,7 +83,7 @@ public class TeaVMTool { private List classesToPreserve = new ArrayList<>(); private TeaVMToolLog log = new EmptyTeaVMToolLog(); private ClassLoader classLoader = TeaVMTool.class.getClassLoader(); - private DiskCachedClassHolderSource cachedClassSource; + private DiskCachedClassReaderSource cachedClassSource; private DiskProgramCache programCache; private DiskMethodNodeCache astCache; private FileSymbolTable symbolTable; @@ -340,7 +340,7 @@ public class TeaVMTool { ClasspathClassHolderSource innerClassSource = new ClasspathClassHolderSource(classLoader, referenceCache); ClassHolderSource classSource = new PreOptimizingClassHolderSource(innerClassSource); - cachedClassSource = new DiskCachedClassHolderSource(cacheDirectory, referenceCache, symbolTable, + cachedClassSource = new DiskCachedClassReaderSource(cacheDirectory, referenceCache, symbolTable, fileTable, variableTable, classSource, innerClassSource); programCache = new DiskProgramCache(cacheDirectory, referenceCache, symbolTable, fileTable, variableTable); diff --git a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java index c428703d3..0a0994f39 100644 --- a/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java +++ b/tools/devserver/src/main/java/org/teavm/devserver/CodeServlet.java @@ -77,6 +77,7 @@ import org.teavm.debugging.information.DebugInformationBuilder; import org.teavm.dependency.FastDependencyAnalyzer; import org.teavm.model.ClassHolder; import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; import org.teavm.model.PreOptimizingClassHolderSource; import org.teavm.model.ReferenceCache; import org.teavm.parsing.ClasspathResourceMapper; @@ -713,12 +714,16 @@ public class CodeServlet extends HttpServlet { private void initBuilder() throws IOException { watcher = new FileSystemWatcher(classPath); - classSource = new MemoryCachedClassReaderSource(referenceCache, symbolTable, fileSymbolTable, - variableSymbolTable); + classSource = createCachedSource(); astCache = new InMemoryMethodNodeCache(referenceCache, symbolTable, fileSymbolTable, variableSymbolTable); programCache = new InMemoryProgramCache(referenceCache, symbolTable, fileSymbolTable, variableSymbolTable); } + private MemoryCachedClassReaderSource createCachedSource() { + return new MemoryCachedClassReaderSource(referenceCache, symbolTable, fileSymbolTable, + variableSymbolTable); + } + private void shutdownBuilder() { try { watcher.dispose(); @@ -757,6 +762,7 @@ public class CodeServlet extends HttpServlet { .setClassLoader(classLoader) .setClassSource(classSource) .setDependencyAnalyzerFactory(FastDependencyAnalyzer::new) + .setClassSourcePacker(this::packClasses) .build(); jsTarget.setStackTraceIncluded(true); @@ -784,6 +790,16 @@ public class CodeServlet extends HttpServlet { postBuild(vm, startTime); } + private ClassReaderSource packClasses(ClassReaderSource source, Collection classNames) { + MemoryCachedClassReaderSource packedSource = createCachedSource(); + packedSource.setProvider(source::get); + for (String className : classNames) { + packedSource.populate(className); + } + packedSource.setProvider(null); + return packedSource; + } + private void addIndicator() { String script = getIndicatorScript(false); try (Writer writer = new OutputStreamWriter(buildTarget.appendToResource(fileName), StandardCharsets.UTF_8)) {