Decrease memory consumption

This commit is contained in:
Alexey Andreev 2019-03-11 17:27:51 +03:00
parent 94322e5f6e
commit a8226ef6a3
27 changed files with 868 additions and 305 deletions

View File

@ -354,7 +354,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
renderingContext.setMinifying(minifying); renderingContext.setMinifying(minifying);
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods, Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
controller.getDiagnostics(), renderingContext); controller.getDiagnostics(), renderingContext);
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter); RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, sourceWriter);
renderer.setProperties(controller.getProperties()); renderer.setProperties(controller.getProperties());
renderer.setMinifying(minifying); renderer.setMinifying(minifying);
renderer.setProgressConsumer(controller::reportProgress); renderer.setProgressConsumer(controller::reportProgress);

View File

@ -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<String, AnnotationValue> fields;
@Override
public String getType() {
return type;
}
@Override
public AnnotationValue getValue(String fieldName) {
return fields.get(fieldName);
}
@Override
public Iterable<String> getAvailableFields() {
return fields.keySet();
}
}

View File

@ -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<String, CachedAnnotation> data;
public CachedAnnotations(Map<String, CachedAnnotation> data) {
this.data = data;
}
@Override
public AnnotationReader get(String type) {
return data.get(type);
}
@Override
public Iterable<? extends AnnotationReader> all() {
return data.values();
}
}

View File

@ -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<String> interfaces;
Map<MethodDescriptor, CachedMethod> methods;
Map<String, CachedField> fields;
@Override
public String getParent() {
return parent;
}
@Override
public Set<String> getInterfaces() {
return interfaces;
}
@Override
public MethodReader getMethod(MethodDescriptor method) {
return methods.get(method);
}
@Override
public Collection<? extends MethodReader> getMethods() {
return methods.values();
}
@Override
public FieldReader getField(String name) {
return fields.get(name);
}
@Override
public Collection<? extends FieldReader> getFields() {
return fields.values();
}
@Override
public String getOwnerName() {
return owner;
}
}

View File

@ -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<ElementModifier> modifiers;
CachedAnnotations annotations;
AccessLevel level;
String name;
@Override
public AccessLevel getLevel() {
return level;
}
@Override
public EnumSet<ElementModifier> 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<ProgramReader> program;
Supplier<ProgramReader> 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;
}
}

View File

@ -15,43 +15,51 @@
*/ */
package org.teavm.cache; package org.teavm.cache;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.teavm.model.AccessLevel; import org.teavm.model.AccessLevel;
import org.teavm.model.AnnotationContainer; import org.teavm.model.AnnotationContainerReader;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue; import org.teavm.model.AnnotationValue;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder; import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodReader;
import org.teavm.model.ReferenceCache; import org.teavm.model.ReferenceCache;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
public class ClassIO { public class ClassIO {
private static AccessLevel[] accessLevels = AccessLevel.values(); private static AccessLevel[] accessLevels = AccessLevel.values();
private static ElementModifier[] elementModifiers = ElementModifier.values(); private static ElementModifier[] elementModifiers = ElementModifier.values();
private ReferenceCache referenceCache;
private SymbolTable symbolTable; private SymbolTable symbolTable;
private ProgramIO programIO; private ProgramIO programIO;
public ClassIO(ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable, public ClassIO(ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable,
SymbolTable varTable) { SymbolTable varTable) {
this.referenceCache = referenceCache;
this.symbolTable = symbolTable; this.symbolTable = symbolTable;
programIO = new ProgramIO(referenceCache, symbolTable, fileTable, varTable); 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); VarDataOutput output = new VarDataOutput(stream);
output.writeUnsigned(cls.getLevel().ordinal()); 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.getParent() != null ? symbolTable.lookup(cls.getParent()) + 1 : 0);
output.writeUnsigned(cls.getOwnerName() != null ? symbolTable.lookup(cls.getOwnerName()) + 1 : 0); output.writeUnsigned(cls.getOwnerName() != null ? symbolTable.lookup(cls.getOwnerName()) + 1 : 0);
output.writeUnsigned(cls.getInterfaces().size()); output.writeUnsigned(cls.getInterfaces().size());
@ -60,56 +68,71 @@ public class ClassIO {
} }
writeAnnotations(output, cls.getAnnotations()); writeAnnotations(output, cls.getAnnotations());
output.writeUnsigned(cls.getFields().size()); output.writeUnsigned(cls.getFields().size());
for (FieldHolder field : cls.getFields()) { for (FieldReader field : cls.getFields()) {
writeField(output, field); writeField(output, field);
} }
output.writeUnsigned(cls.getMethods().size()); output.writeUnsigned(cls.getMethods().size());
for (MethodHolder method : cls.getMethods()) { for (MethodReader method : cls.getMethods()) {
writeMethod(output, method); 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); VarDataInput input = new VarDataInput(stream);
ClassHolder cls = new ClassHolder(name); CachedClassReader cls = new CachedClassReader();
cls.setLevel(accessLevels[input.readUnsigned()]); cls.name = name;
cls.getModifiers().addAll(unpackModifiers(input.readUnsigned())); cls.level = accessLevels[input.readUnsigned()];
cls.modifiers = unpackModifiers(input.readUnsigned());
int parentIndex = 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(); 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(); int ifaceCount = input.readUnsigned();
Set<String> interfaces = new LinkedHashSet<>();
for (int i = 0; i < ifaceCount; ++i) { 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<String, CachedField> fields = new LinkedHashMap<>();
int fieldCount = input.readUnsigned(); int fieldCount = input.readUnsigned();
for (int i = 0; i < fieldCount; ++i) { 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<MethodDescriptor, CachedMethod> methods = new LinkedHashMap<>();
int methodCount = input.readUnsigned(); int methodCount = input.readUnsigned();
for (int i = 0; i < methodCount; ++i) { 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; 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.getName()));
output.writeUnsigned(symbolTable.lookup(field.getType().toString())); output.writeUnsigned(symbolTable.lookup(field.getType().toString()));
output.writeUnsigned(field.getLevel().ordinal()); output.writeUnsigned(field.getLevel().ordinal());
output.writeUnsigned(packModifiers(field.getModifiers())); output.writeUnsigned(packModifiers(field.readModifiers()));
writeFieldValue(output, field.getInitialValue()); writeFieldValue(output, field.getInitialValue());
writeAnnotations(output, field.getAnnotations()); writeAnnotations(output, field.getAnnotations());
} }
private FieldHolder readField(VarDataInput input) throws IOException { private CachedField readField(String className, VarDataInput input) throws IOException {
FieldHolder field = new FieldHolder(symbolTable.at(input.readUnsigned())); CachedField field = new CachedField();
field.setType(ValueType.parse(symbolTable.at(input.readUnsigned()))); field.name = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
field.setLevel(accessLevels[input.readUnsigned()]); field.type = referenceCache.getCached(ValueType.parse(symbolTable.at(input.readUnsigned())));
field.getModifiers().addAll(unpackModifiers(input.readUnsigned())); field.level = accessLevels[input.readUnsigned()];
field.setInitialValue(readFieldValue(input)); field.modifiers = unpackModifiers(input.readUnsigned());
readAnnotations(input, field.getAnnotations()); field.initialValue = readFieldValue(input);
field.annotations = readAnnotations(input);
field.ownerName = className;
field.reference = referenceCache.getCached(new FieldReference(className, field.name));
return field; 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(symbolTable.lookup(method.getDescriptor().toString()));
output.writeUnsigned(method.getLevel().ordinal()); output.writeUnsigned(method.getLevel().ordinal());
output.writeUnsigned(packModifiers(method.getModifiers())); output.writeUnsigned(packModifiers(method.readModifiers()));
writeAnnotations(output, method.getAnnotations()); writeAnnotations(output, method.getAnnotations());
for (AnnotationContainer parameterAnnotation : method.getParameterAnnotations()) { for (AnnotationContainerReader parameterAnnotation : method.getParameterAnnotations()) {
writeAnnotations(output, parameterAnnotation); writeAnnotations(output, parameterAnnotation);
} }
@ -173,49 +196,68 @@ public class ClassIO {
if (method.getProgram() != null) { if (method.getProgram() != null) {
output.writeUnsigned(1); 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 { } else {
output.writeUnsigned(0); output.writeUnsigned(0);
} }
} }
private MethodHolder readMethod(VarDataInput input) throws IOException { private CachedMethod readMethod(String className, VarDataInput input) throws IOException {
MethodHolder method = new MethodHolder(MethodDescriptor.parse(symbolTable.at(input.readUnsigned()))); CachedMethod method = new CachedMethod();
method.setLevel(accessLevels[input.readUnsigned()]); MethodDescriptor descriptor = referenceCache.getCached(
method.getModifiers().addAll(unpackModifiers(input.readUnsigned())); MethodDescriptor.parse(symbolTable.at(input.readUnsigned())));
readAnnotations(input, method.getAnnotations()); 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) { for (int i = 0; i < method.parameterCount(); ++i) {
readAnnotations(input, method.parameterAnnotation(i)); method.parameterAnnotations[i] = readAnnotations(input);
} }
if (input.readUnsigned() != 0) { if (input.readUnsigned() != 0) {
method.setAnnotationDefault(readAnnotationValue(input)); method.annotationDefault = readAnnotationValue(input);
} }
if (input.readUnsigned() != 0) { 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; return method;
} }
private void writeAnnotations(VarDataOutput output, AnnotationContainer annotations) throws IOException { private void writeAnnotations(VarDataOutput output, AnnotationContainerReader annotations) throws IOException {
List<AnnotationHolder> annotationList = new ArrayList<>(); List<AnnotationReader> annotationList = new ArrayList<>();
for (AnnotationHolder annot : annotations.all()) { for (AnnotationReader annot : annotations.all()) {
annotationList.add(annot); annotationList.add(annot);
} }
output.writeUnsigned(annotationList.size()); output.writeUnsigned(annotationList.size());
for (AnnotationHolder annot : annotationList) { for (AnnotationReader annot : annotationList) {
writeAnnotation(output, annot); writeAnnotation(output, annot);
} }
} }
private void readAnnotations(VarDataInput input, AnnotationContainer annotations) throws IOException { private CachedAnnotations readAnnotations(VarDataInput input) throws IOException {
Map<String, CachedAnnotation> annotations = new HashMap<>();
int annotCount = input.readUnsigned(); int annotCount = input.readUnsigned();
for (int i = 0; i < annotCount; ++i) { for (int i = 0; i < annotCount; ++i) {
AnnotationHolder annot = readAnnotation(input); CachedAnnotation annot = readAnnotation(input);
annotations.add(annot); annotations.put(annot.type, annot);
} }
return new CachedAnnotations(annotations);
} }
private void writeAnnotation(VarDataOutput output, AnnotationReader annotation) throws IOException { private void writeAnnotation(VarDataOutput output, AnnotationReader annotation) throws IOException {
@ -231,14 +273,17 @@ public class ClassIO {
} }
} }
private AnnotationHolder readAnnotation(VarDataInput input) throws IOException { private CachedAnnotation readAnnotation(VarDataInput input) throws IOException {
AnnotationHolder annotation = new AnnotationHolder(symbolTable.at(input.readUnsigned())); CachedAnnotation annotation = new CachedAnnotation();
annotation.type = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
int valueCount = input.readUnsigned(); int valueCount = input.readUnsigned();
Map<String, AnnotationValue> fields = new HashMap<>();
for (int i = 0; i < valueCount; ++i) { 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); AnnotationValue value = readAnnotationValue(input);
annotation.getValues().put(name, value); fields.put(name, value);
} }
annotation.fields = fields;
return annotation; return annotation;
} }
@ -300,13 +345,14 @@ public class ClassIO {
case AnnotationValue.BYTE: case AnnotationValue.BYTE:
return new AnnotationValue((byte) input.readSigned()); return new AnnotationValue((byte) input.readSigned());
case AnnotationValue.CLASS: 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: case AnnotationValue.DOUBLE:
return new AnnotationValue(input.readDouble()); return new AnnotationValue(input.readDouble());
case AnnotationValue.ENUM: { case AnnotationValue.ENUM: {
String className = symbolTable.at(input.readUnsigned()); String className = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
String fieldName = symbolTable.at(input.readUnsigned()); String fieldName = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
return new AnnotationValue(new FieldReference(className, fieldName)); return new AnnotationValue(referenceCache.getCached(new FieldReference(className, fieldName)));
} }
case AnnotationValue.FLOAT: case AnnotationValue.FLOAT:
return new AnnotationValue(input.readFloat()); return new AnnotationValue(input.readFloat());
@ -339,8 +385,8 @@ public class ClassIO {
return result; return result;
} }
private Set<ElementModifier> unpackModifiers(int packed) { private EnumSet<ElementModifier> unpackModifiers(int packed) {
Set<ElementModifier> modifiers = EnumSet.noneOf(ElementModifier.class); EnumSet<ElementModifier> modifiers = EnumSet.noneOf(ElementModifier.class);
while (packed != 0) { while (packed != 0) {
int n = Integer.numberOfTrailingZeros(packed); int n = Integer.numberOfTrailingZeros(packed);
packed ^= 1 << n; packed ^= 1 << n;

View File

@ -28,13 +28,14 @@ import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderSource; import org.teavm.model.ClassHolderSource;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ReferenceCache; import org.teavm.model.ReferenceCache;
import org.teavm.parsing.ClassDateProvider; import org.teavm.parsing.ClassDateProvider;
public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStatus { public class DiskCachedClassReaderSource implements ClassReaderSource, CacheStatus {
private File directory; private File directory;
private ClassHolderSource innerSource; private ClassHolderSource innerSource;
private ClassDateProvider classDateProvider; private ClassDateProvider classDateProvider;
@ -42,7 +43,7 @@ public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStat
private Set<String> newClasses = new HashSet<>(); private Set<String> newClasses = new HashSet<>();
private ClassIO classIO; 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, SymbolTable fileTable, SymbolTable variableTable, ClassHolderSource innerSource,
ClassDateProvider classDateProvider) { ClassDateProvider classDateProvider) {
this.directory = directory; this.directory = directory;
@ -52,7 +53,7 @@ public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStat
} }
@Override @Override
public ClassHolder get(String name) { public ClassReader get(String name) {
return getItemFromCache(name).cls; return getItemFromCache(name).cls;
} }
@ -93,7 +94,7 @@ public class DiskCachedClassHolderSource implements ClassHolderSource, CacheStat
} }
private static class Item { private static class Item {
ClassHolder cls; ClassReader cls;
boolean dirty; boolean dirty;
} }

View File

@ -18,14 +18,12 @@ package org.teavm.cache;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -33,7 +31,7 @@ import org.teavm.model.ReferenceCache;
public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheStatus { public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheStatus {
private Map<String, Entry> cache = new HashMap<>(); private Map<String, Entry> cache = new HashMap<>();
private Function<String, ClassHolder> provider; private Function<String, ClassReader> provider;
private ClassIO classIO; private ClassIO classIO;
private final Set<String> freshClasses = new HashSet<>(); private final Set<String> freshClasses = new HashSet<>();
@ -42,7 +40,7 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt
classIO = new ClassIO(referenceCache, symbolTable, fileTable, varTable); classIO = new ClassIO(referenceCache, symbolTable, fileTable, varTable);
} }
public void setProvider(Function<String, ClassHolder> provider) { public void setProvider(Function<String, ClassReader> provider) {
this.provider = provider; this.provider = provider;
} }
@ -56,10 +54,33 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt
return isStaleClass(method.getClassName()); return isStaleClass(method.getClassName());
} }
public void populate(String name) {
getEntry(name);
}
@Override @Override
public ClassReader get(String name) { public ClassReader get(String name) {
Entry entry = cache.computeIfAbsent(name, className -> { Entry entry = getEntry(name);
ClassHolder cls = provider.apply(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(); Entry en = new Entry();
if (cls != null) { if (cls != null) {
ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteArrayOutputStream output = new ByteArrayOutputStream();
@ -69,26 +90,9 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt
throw new RuntimeException(e); throw new RuntimeException(e);
} }
en.data = output.toByteArray(); en.data = output.toByteArray();
en.reader = new WeakReference<>(cls);
} }
return en; 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() { public void commit() {
@ -107,6 +111,6 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt
class Entry { class Entry {
byte[] data; byte[] data;
WeakReference<ClassReader> reader; ClassReader reader;
} }
} }

View File

@ -18,23 +18,29 @@ package org.teavm.cache;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Objects; import java.util.List;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.BasicBlockReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.Incoming; import org.teavm.model.Incoming;
import org.teavm.model.IncomingReader;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.InvokeDynamicInstruction; import org.teavm.model.InvokeDynamicInstruction;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHandle; import org.teavm.model.MethodHandle;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Phi; import org.teavm.model.Phi;
import org.teavm.model.PhiReader;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.ProgramReader;
import org.teavm.model.ReferenceCache; import org.teavm.model.ReferenceCache;
import org.teavm.model.RuntimeConstant; import org.teavm.model.RuntimeConstant;
import org.teavm.model.TextLocation; import org.teavm.model.TextLocation;
import org.teavm.model.TryCatchBlock; import org.teavm.model.TryCatchBlock;
import org.teavm.model.TryCatchBlockReader;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.Variable; import org.teavm.model.Variable;
import org.teavm.model.VariableReader;
import org.teavm.model.instructions.ArrayElementType; import org.teavm.model.instructions.ArrayElementType;
import org.teavm.model.instructions.ArrayLengthInstruction; import org.teavm.model.instructions.ArrayLengthInstruction;
import org.teavm.model.instructions.AssignInstruction; 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.GetElementInstruction;
import org.teavm.model.instructions.GetFieldInstruction; import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InitClassInstruction; 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.IntegerConstantInstruction;
import org.teavm.model.instructions.IntegerSubtype; import org.teavm.model.instructions.IntegerSubtype;
import org.teavm.model.instructions.InvocationType; 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.StringConstantInstruction;
import org.teavm.model.instructions.SwitchInstruction; import org.teavm.model.instructions.SwitchInstruction;
import org.teavm.model.instructions.SwitchTableEntry; import org.teavm.model.instructions.SwitchTableEntry;
import org.teavm.model.instructions.SwitchTableEntryReader;
import org.teavm.model.instructions.UnwrapArrayInstruction; import org.teavm.model.instructions.UnwrapArrayInstruction;
public class ProgramIO { public class ProgramIO {
@ -103,62 +110,42 @@ public class ProgramIO {
this.variableTable = variableTable; 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)); 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.variableCount());
data.writeUnsigned(program.basicBlockCount()); data.writeUnsigned(program.basicBlockCount());
for (int i = 0; i < program.variableCount(); ++i) { 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.getRegister());
data.writeUnsigned(var.getDebugName() != null ? variableTable.lookup(var.getDebugName()) + 1 : 0); data.writeUnsigned(var.getDebugName() != null ? variableTable.lookup(var.getDebugName()) + 1 : 0);
} }
for (int i = 0; i < program.basicBlockCount(); ++i) { for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock basicBlock = program.basicBlockAt(i); BasicBlockReader basicBlock = program.basicBlockAt(i);
data.writeUnsigned(basicBlock.getExceptionVariable() != null data.writeUnsigned(basicBlock.getExceptionVariable() != null
? basicBlock.getExceptionVariable().getIndex() + 1 : 0); ? basicBlock.getExceptionVariable().getIndex() + 1 : 0);
data.writeUnsigned(basicBlock.getPhis().size()); data.writeUnsigned(basicBlock.readPhis().size());
data.writeUnsigned(basicBlock.getTryCatchBlocks().size()); data.writeUnsigned(basicBlock.readTryCatchBlocks().size());
for (Phi phi : basicBlock.getPhis()) { for (PhiReader phi : basicBlock.readPhis()) {
data.writeUnsigned(phi.getReceiver().getIndex()); data.writeUnsigned(phi.getReceiver().getIndex());
data.writeUnsigned(phi.getIncomings().size()); data.writeUnsigned(phi.readIncomings().size());
for (Incoming incoming : phi.getIncomings()) { for (IncomingReader incoming : phi.readIncomings()) {
data.writeUnsigned(incoming.getSource().getIndex()); data.writeUnsigned(incoming.getSource().getIndex());
data.writeUnsigned(incoming.getValue().getIndex()); data.writeUnsigned(incoming.getValue().getIndex());
} }
} }
for (TryCatchBlock tryCatch : basicBlock.getTryCatchBlocks()) { for (TryCatchBlockReader tryCatch : basicBlock.readTryCatchBlocks()) {
data.writeUnsigned(tryCatch.getExceptionType() != null ? symbolTable.lookup( data.writeUnsigned(tryCatch.getExceptionType() != null ? symbolTable.lookup(
tryCatch.getExceptionType()) + 1 : 0); tryCatch.getExceptionType()) + 1 : 0);
data.writeUnsigned(tryCatch.getHandler().getIndex()); data.writeUnsigned(tryCatch.getHandler().getIndex());
} }
TextLocation location = null;
InstructionWriter insnWriter = new InstructionWriter(data); InstructionWriter insnWriter = new InstructionWriter(data);
for (Instruction insn : basicBlock) { try {
try { basicBlock.readAllInstructions(insnWriter);
if (!Objects.equals(location, insn.getLocation())) { } catch (IOExceptionWrapper e) {
TextLocation newLocation = insn.getLocation(); throw (IOException) e.getCause();
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();
}
} }
data.writeUnsigned(0); data.writeUnsigned(0);
} }
@ -246,15 +233,38 @@ public class ProgramIO {
return program; return program;
} }
private class InstructionWriter implements InstructionVisitor { private class InstructionWriter implements InstructionReader {
private VarDataOutput output; private VarDataOutput output;
TextLocation location;
InstructionWriter(VarDataOutput output) { InstructionWriter(VarDataOutput output) {
this.output = output; this.output = output;
} }
@Override @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 { try {
output.writeUnsigned(3); output.writeUnsigned(3);
} catch (IOException e) { } catch (IOException e) {
@ -263,196 +273,202 @@ public class ProgramIO {
} }
@Override @Override
public void visit(ClassConstantInstruction insn) { public void classConstant(VariableReader receiver, ValueType cst) {
try { try {
output.writeUnsigned(4); output.writeUnsigned(4);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(symbolTable.lookup(insn.getConstant().toString())); output.writeUnsigned(symbolTable.lookup(cst.toString()));
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(NullConstantInstruction insn) { public void nullConstant(VariableReader receiver) {
try { try {
output.writeUnsigned(5); output.writeUnsigned(5);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(IntegerConstantInstruction insn) { public void integerConstant(VariableReader receiver, int cst) {
try { try {
output.writeUnsigned(6); output.writeUnsigned(6);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeSigned(insn.getConstant()); output.writeSigned(cst);
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(LongConstantInstruction insn) { public void longConstant(VariableReader receiver, long cst) {
try { try {
output.writeUnsigned(7); output.writeUnsigned(7);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeSigned(insn.getConstant()); output.writeSigned(cst);
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(FloatConstantInstruction insn) { public void floatConstant(VariableReader receiver, float cst) {
try { try {
output.writeUnsigned(8); output.writeUnsigned(8);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeFloat(insn.getConstant()); output.writeFloat(cst);
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(DoubleConstantInstruction insn) { public void doubleConstant(VariableReader receiver, double cst) {
try { try {
output.writeUnsigned(9); output.writeUnsigned(9);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeDouble(insn.getConstant()); output.writeDouble(cst);
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(StringConstantInstruction insn) { public void stringConstant(VariableReader receiver, String cst) {
try { try {
output.writeUnsigned(10); output.writeUnsigned(10);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.write(insn.getConstant()); output.write(cst);
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(BinaryInstruction insn) { public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
NumericOperandType type) {
try { try {
output.writeUnsigned(11 + insn.getOperation().ordinal()); output.writeUnsigned(11 + op.ordinal());
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getOperandType().ordinal()); output.writeUnsigned(type.ordinal());
output.writeUnsigned(insn.getFirstOperand().getIndex()); output.writeUnsigned(first.getIndex());
output.writeUnsigned(insn.getSecondOperand().getIndex()); output.writeUnsigned(second.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(NegateInstruction insn) { public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) {
try { try {
output.writeUnsigned(23); output.writeUnsigned(23);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getOperandType().ordinal()); output.writeUnsigned(type.ordinal());
output.writeUnsigned(insn.getOperand().getIndex()); output.writeUnsigned(operand.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(AssignInstruction insn) { public void assign(VariableReader receiver, VariableReader assignee) {
try { try {
output.writeUnsigned(24); output.writeUnsigned(24);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getAssignee().getIndex()); output.writeUnsigned(assignee.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(CastInstruction insn) { public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
try { try {
output.writeUnsigned(25); output.writeUnsigned(25);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(symbolTable.lookup(insn.getTargetType().toString())); output.writeUnsigned(symbolTable.lookup(targetType.toString()));
output.writeUnsigned(insn.getValue().getIndex()); output.writeUnsigned(value.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(CastNumberInstruction insn) { public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType,
NumericOperandType targetType) {
try { try {
output.writeUnsigned(26); output.writeUnsigned(26);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getSourceType().ordinal() | (insn.getTargetType().ordinal() << 2)); output.writeUnsigned(sourceType.ordinal() | (targetType.ordinal() << 2));
output.writeUnsigned(insn.getValue().getIndex()); output.writeUnsigned(value.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(CastIntegerInstruction insn) { public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type,
CastIntegerDirection direction) {
try { try {
output.writeUnsigned(27); output.writeUnsigned(27);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getDirection().ordinal() | (insn.getTargetType().ordinal() << 1)); output.writeUnsigned(direction.ordinal() | (type.ordinal() << 1));
output.writeUnsigned(insn.getValue().getIndex()); output.writeUnsigned(value.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(BranchingInstruction insn) { public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent,
BasicBlockReader alternative) {
try { try {
output.writeUnsigned(28 + insn.getCondition().ordinal()); output.writeUnsigned(28 + cond.ordinal());
output.writeUnsigned(insn.getOperand().getIndex()); output.writeUnsigned(operand.getIndex());
output.writeUnsigned(insn.getConsequent().getIndex()); output.writeUnsigned(consequent.getIndex());
output.writeUnsigned(insn.getAlternative().getIndex()); output.writeUnsigned(alternative.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(BinaryBranchingInstruction insn) { public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second,
BasicBlockReader consequent, BasicBlockReader alternative) {
try { try {
output.writeUnsigned(36 + insn.getCondition().ordinal()); output.writeUnsigned(36 + cond.ordinal());
output.writeUnsigned(insn.getFirstOperand().getIndex()); output.writeUnsigned(first.getIndex());
output.writeUnsigned(insn.getSecondOperand().getIndex()); output.writeUnsigned(second.getIndex());
output.writeUnsigned(insn.getConsequent().getIndex()); output.writeUnsigned(consequent.getIndex());
output.writeUnsigned(insn.getAlternative().getIndex()); output.writeUnsigned(alternative.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(JumpInstruction insn) { public void jump(BasicBlockReader target) {
try { try {
output.writeUnsigned(40); output.writeUnsigned(40);
output.writeUnsigned(insn.getTarget().getIndex()); output.writeUnsigned(target.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(SwitchInstruction insn) { public void choose(VariableReader condition, List<? extends SwitchTableEntryReader> table,
BasicBlockReader defaultTarget) {
try { try {
output.writeUnsigned(41); output.writeUnsigned(41);
output.writeUnsigned(insn.getCondition().getIndex()); output.writeUnsigned(condition.getIndex());
output.writeUnsigned(insn.getDefaultTarget().getIndex()); output.writeUnsigned(defaultTarget.getIndex());
output.writeUnsigned(insn.getEntries().size()); output.writeUnsigned(table.size());
for (SwitchTableEntry entry : insn.getEntries()) { for (SwitchTableEntryReader entry : table) {
output.writeSigned(entry.getCondition()); output.writeSigned(entry.getCondition());
output.writeUnsigned(entry.getTarget().getIndex()); output.writeUnsigned(entry.getTarget().getIndex());
} }
@ -462,11 +478,11 @@ public class ProgramIO {
} }
@Override @Override
public void visit(ExitInstruction insn) { public void exit(VariableReader valueToReturn) {
try { try {
if (insn.getValueToReturn() != null) { if (valueToReturn != null) {
output.writeUnsigned(42); output.writeUnsigned(42);
output.writeUnsigned(insn.getValueToReturn().getIndex()); output.writeUnsigned(valueToReturn.getIndex());
} else { } else {
output.writeUnsigned(43); output.writeUnsigned(43);
} }
@ -476,46 +492,36 @@ public class ProgramIO {
} }
@Override @Override
public void visit(RaiseInstruction insn) { public void raise(VariableReader exception) {
try { try {
output.writeUnsigned(44); output.writeUnsigned(44);
output.writeUnsigned(insn.getException().getIndex()); output.writeUnsigned(exception.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(ConstructArrayInstruction insn) { public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
try { try {
output.writeUnsigned(45); output.writeUnsigned(45);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(symbolTable.lookup(insn.getItemType().toString())); output.writeUnsigned(symbolTable.lookup(itemType.toString()));
output.writeUnsigned(insn.getSize().getIndex()); output.writeUnsigned(size.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(ConstructInstruction insn) { public void createArray(VariableReader receiver, ValueType itemType,
try { List<? extends VariableReader> dimensions) {
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) {
try { try {
output.writeUnsigned(47); output.writeUnsigned(47);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(symbolTable.lookup(insn.getItemType().toString())); output.writeUnsigned(symbolTable.lookup(itemType.toString()));
output.writeUnsigned(insn.getDimensions().size()); output.writeUnsigned(dimensions.size());
for (Variable dimension : insn.getDimensions()) { for (VariableReader dimension : dimensions) {
output.writeUnsigned(dimension.getIndex()); output.writeUnsigned(dimension.getIndex());
} }
} catch (IOException e) { } catch (IOException e) {
@ -524,113 +530,128 @@ public class ProgramIO {
} }
@Override @Override
public void visit(GetFieldInstruction insn) { public void create(VariableReader receiver, String type) {
try { try {
output.writeUnsigned(insn.getInstance() != null ? 48 : 49); output.writeUnsigned(46);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
if (insn.getInstance() != null) { output.writeUnsigned(symbolTable.lookup(type));
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()));
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(PutFieldInstruction insn) { public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
ValueType fieldType) {
try { try {
output.writeUnsigned(insn.getInstance() != null ? 50 : 51); output.writeUnsigned(instance != null ? 48 : 49);
if (insn.getInstance() != null) { output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getInstance().getIndex()); if (instance != null) {
output.writeUnsigned(instance.getIndex());
} }
output.writeUnsigned(symbolTable.lookup(insn.getField().getClassName())); output.writeUnsigned(symbolTable.lookup(field.getClassName()));
output.writeUnsigned(symbolTable.lookup(insn.getField().getFieldName())); output.writeUnsigned(symbolTable.lookup(field.getFieldName()));
output.writeUnsigned(symbolTable.lookup(insn.getFieldType().toString())); output.writeUnsigned(symbolTable.lookup(fieldType.toString()));
output.writeUnsigned(insn.getValue().getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @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 { try {
output.writeUnsigned(52); output.writeUnsigned(52);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getArray().getIndex()); output.writeUnsigned(array.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(CloneArrayInstruction insn) { public void cloneArray(VariableReader receiver, VariableReader array) {
try { try {
output.writeUnsigned(53); output.writeUnsigned(53);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getArray().getIndex()); output.writeUnsigned(array.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(UnwrapArrayInstruction insn) { public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
try { try {
output.writeUnsigned(54 + insn.getElementType().ordinal()); output.writeUnsigned(54 + elementType.ordinal());
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getArray().getIndex()); output.writeUnsigned(array.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(GetElementInstruction insn) { public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
ArrayElementType elementType) {
try { try {
output.writeUnsigned(62 + insn.getType().ordinal()); output.writeUnsigned(62 + elementType.ordinal());
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getArray().getIndex()); output.writeUnsigned(array.getIndex());
output.writeUnsigned(insn.getIndex().getIndex()); output.writeUnsigned(index.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(PutElementInstruction insn) { public void putElement(VariableReader array, VariableReader index, VariableReader value,
ArrayElementType elementType) {
try { try {
output.writeUnsigned(70 + insn.getType().ordinal()); output.writeUnsigned(70 + elementType.ordinal());
output.writeUnsigned(insn.getArray().getIndex()); output.writeUnsigned(array.getIndex());
output.writeUnsigned(insn.getIndex().getIndex()); output.writeUnsigned(index.getIndex());
output.writeUnsigned(insn.getValue().getIndex()); output.writeUnsigned(value.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(InvokeInstruction insn) { public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments, InvocationType type) {
try { try {
switch (insn.getType()) { switch (type) {
case SPECIAL: case SPECIAL:
output.writeUnsigned(insn.getInstance() == null ? 78 : 79); output.writeUnsigned(instance == null ? 78 : 79);
break; break;
case VIRTUAL: case VIRTUAL:
output.writeUnsigned(80); output.writeUnsigned(80);
break; break;
} }
output.writeUnsigned(insn.getReceiver() != null ? insn.getReceiver().getIndex() + 1 : 0); output.writeUnsigned(receiver != null ? receiver.getIndex() + 1 : 0);
if (insn.getInstance() != null) { if (instance != null) {
output.writeUnsigned(insn.getInstance().getIndex()); output.writeUnsigned(instance.getIndex());
} }
output.writeUnsigned(symbolTable.lookup(insn.getMethod().getClassName())); output.writeUnsigned(symbolTable.lookup(method.getClassName()));
output.writeUnsigned(symbolTable.lookup(insn.getMethod().getDescriptor().toString())); output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
for (int i = 0; i < insn.getArguments().size(); ++i) { for (int i = 0; i < arguments.size(); ++i) {
output.writeUnsigned(insn.getArguments().get(i).getIndex()); output.writeUnsigned(arguments.get(i).getIndex());
} }
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
@ -638,19 +659,21 @@ public class ProgramIO {
} }
@Override @Override
public void visit(InvokeDynamicInstruction insn) { public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
List<RuntimeConstant> bootstrapArguments) {
try { try {
output.writeUnsigned(81); output.writeUnsigned(81);
output.writeUnsigned(insn.getReceiver() != null ? insn.getReceiver().getIndex() + 1 : 0); output.writeUnsigned(receiver != null ? receiver.getIndex() + 1 : 0);
output.writeUnsigned(insn.getInstance() != null ? insn.getInstance().getIndex() + 1 : 0); output.writeUnsigned(instance != null ? instance.getIndex() + 1 : 0);
output.writeUnsigned(symbolTable.lookup(insn.getMethod().toString())); output.writeUnsigned(symbolTable.lookup(method.toString()));
for (int i = 0; i < insn.getArguments().size(); ++i) { for (int i = 0; i < arguments.size(); ++i) {
output.writeUnsigned(insn.getArguments().get(i).getIndex()); output.writeUnsigned(arguments.get(i).getIndex());
} }
write(insn.getBootstrapMethod()); write(bootstrapMethod);
output.writeUnsigned(insn.getBootstrapArguments().size()); output.writeUnsigned(bootstrapArguments.size());
for (int i = 0; i < insn.getBootstrapArguments().size(); ++i) { for (int i = 0; i < bootstrapArguments.size(); ++i) {
write(insn.getBootstrapArguments().get(i)); write(bootstrapArguments.get(i));
} }
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
@ -658,53 +681,53 @@ public class ProgramIO {
} }
@Override @Override
public void visit(IsInstanceInstruction insn) { public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
try { try {
output.writeUnsigned(82); output.writeUnsigned(82);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(symbolTable.lookup(insn.getType().toString())); output.writeUnsigned(symbolTable.lookup(type.toString()));
output.writeUnsigned(insn.getValue().getIndex()); output.writeUnsigned(value.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(InitClassInstruction insn) { public void initClass(String className) {
try { try {
output.writeUnsigned(83); output.writeUnsigned(83);
output.writeUnsigned(symbolTable.lookup(insn.getClassName())); output.writeUnsigned(symbolTable.lookup(className));
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(NullCheckInstruction insn) { public void nullCheck(VariableReader receiver, VariableReader value) {
try { try {
output.writeUnsigned(84); output.writeUnsigned(84);
output.writeUnsigned(insn.getReceiver().getIndex()); output.writeUnsigned(receiver.getIndex());
output.writeUnsigned(insn.getValue().getIndex()); output.writeUnsigned(value.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(MonitorEnterInstruction insn) { public void monitorEnter(VariableReader objectRef) {
try { try {
output.writeUnsigned(85); output.writeUnsigned(85);
output.writeUnsigned(insn.getObjectRef().getIndex()); output.writeUnsigned(objectRef.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }
} }
@Override @Override
public void visit(MonitorExitInstruction insn) { public void monitorExit(VariableReader objectRef) {
try { try {
output.writeUnsigned(86); output.writeUnsigned(86);
output.writeUnsigned(insn.getObjectRef().getIndex()); output.writeUnsigned(objectRef.getIndex());
} catch (IOException e) { } catch (IOException e) {
throw new IOExceptionWrapper(e); throw new IOExceptionWrapper(e);
} }

View File

@ -124,4 +124,24 @@ public class VarDataInput {
} }
return new String(chars); 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;
}
} }

View File

@ -97,6 +97,11 @@ public class VarDataOutput implements Closeable {
} }
} }
public void writeBytes(byte[] data) throws IOException {
writeUnsigned(data.length);
output.write(data);
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
output.close(); output.close();

View File

@ -22,6 +22,7 @@ public class ClassDependency implements ClassDependencyInfo {
private DependencyAnalyzer analyzer; private DependencyAnalyzer analyzer;
private String className; private String className;
private ClassReader classReader; private ClassReader classReader;
boolean present;
boolean activated; boolean activated;
ClassDependency(DependencyAnalyzer analyzer, String className, ClassReader classReader) { ClassDependency(DependencyAnalyzer analyzer, String className, ClassReader classReader) {
@ -37,7 +38,7 @@ public class ClassDependency implements ClassDependencyInfo {
@Override @Override
public boolean isMissing() { public boolean isMissing() {
return classReader == null; return classReader == null && !present;
} }
public ClassReader getClassReader() { public ClassReader getClassReader() {
@ -49,4 +50,11 @@ public class ClassDependency implements ClassDependencyInfo {
analyzer.initClass(this, location); analyzer.initClass(this, location);
} }
} }
void cleanup() {
if (classReader != null) {
present = true;
classReader = null;
}
}
} }

View File

@ -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<? extends String> classNames);
}

View File

@ -80,7 +80,7 @@ public class DependencyAgent implements DependencyInfo, ServiceRepository {
@Override @Override
public ClassReaderSource getClassSource() { public ClassReaderSource getClassSource() {
return analyzer.getClassSource(); return analyzer.agentClassSource;
} }
@Override @Override

View File

@ -26,6 +26,7 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Deque; import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -82,8 +83,10 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
private int classNameSuffix; private int classNameSuffix;
private ClassReaderSource unprocessedClassSource; private ClassReaderSource unprocessedClassSource;
private DependencyClassSource classSource; private DependencyClassSource classSource;
ClassReaderSource agentClassSource;
private ClassLoader classLoader; private ClassLoader classLoader;
private Map<String, Map<MethodDescriptor, Optional<MethodHolder>>> methodReaderCache = new HashMap<>(1000, 0.5f); private Map<String, Map<MethodDescriptor, Optional<MethodHolder>>> methodReaderCache = new HashMap<>(1000, 0.5f);
private Map<MethodReference, MethodDependency> implementationCache = new HashMap<>();
private Function<FieldReference, FieldHolder> fieldReaderCache; private Function<FieldReference, FieldHolder> fieldReaderCache;
private Map<String, Map<MethodDescriptor, MethodDependency>> methodCache = new HashMap<>(); private Map<String, Map<MethodDescriptor, MethodDependency>> methodCache = new HashMap<>();
private Set<MethodReference> reachedMethods = new LinkedHashSet<>(); private Set<MethodReference> reachedMethods = new LinkedHashSet<>();
@ -111,6 +114,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
IncrementalCache incrementalCache = new IncrementalCache(); IncrementalCache incrementalCache = new IncrementalCache();
boolean asyncSupported; boolean asyncSupported;
private ReferenceCache referenceCache; private ReferenceCache referenceCache;
private Set<String> generatedClassNames = new HashSet<>();
DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
Diagnostics diagnostics, ReferenceCache referenceCache) { Diagnostics diagnostics, ReferenceCache referenceCache) {
@ -118,6 +122,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
this.referenceCache = referenceCache; this.referenceCache = referenceCache;
this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache); this.classSource = new DependencyClassSource(classSource, diagnostics, incrementalCache);
agentClassSource = this.classSource;
classHierarchy = new ClassHierarchy(this.classSource); classHierarchy = new ClassHierarchy(this.classSource);
this.classLoader = classLoader; this.classLoader = classLoader;
this.services = services; this.services = services;
@ -206,11 +211,11 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
@Override @Override
public ClassReaderSource getClassSource() { public ClassReaderSource getClassSource() {
return classSource; return classSource != null ? classSource : agentClassSource;
} }
public boolean isSynthesizedClass(String className) { public boolean isSynthesizedClass(String className) {
return classSource.isGeneratedClass(className); return classSource != null ? classSource.isGeneratedClass(className) : generatedClassNames.contains(className);
} }
public ClassHierarchy getClassHierarchy() { public ClassHierarchy getClassHierarchy() {
@ -608,8 +613,10 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
@Override @Override
public MethodDependency getMethodImplementation(MethodReference methodRef) { public MethodDependency getMethodImplementation(MethodReference methodRef) {
MethodReader method = getMethodHolder(methodRef.getClassName(), methodRef.getDescriptor()); return implementationCache.computeIfAbsent(methodRef, m -> {
return method != null ? getMethod(method.getReference()) : null; MethodReader resolved = agentClassSource.resolveImplementation(m);
return resolved != null ? getMethod(resolved.getReference()) : null;
});
} }
private MethodHolder getMethodHolder(String className, MethodDescriptor descriptor) { private MethodHolder getMethodHolder(String className, MethodDescriptor descriptor) {
@ -726,7 +733,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
System.out.println("Total domains: " + domainCount); System.out.println("Total domains: " + domainCount);
} }
public void cleanup() { public void cleanup(ClassSourcePacker classSourcePacker) {
for (DependencyNode node : allNodes) { for (DependencyNode node : allNodes) {
node.followers = null; node.followers = null;
node.transitions = null; node.transitions = null;
@ -744,6 +751,7 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
for (MethodDependency methodDependency : map.values()) { for (MethodDependency methodDependency : map.values()) {
methodDependency.locationListeners = null; methodDependency.locationListeners = null;
methodDependency.locations = null; methodDependency.locations = null;
methodDependency.cleanup();
} }
} }
@ -752,15 +760,30 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
if (field != null) { if (field != null) {
field.locationListeners = null; field.locationListeners = null;
field.locations = null; field.locations = null;
field.cleanup();
} }
} }
for (String className : classCache.getCachedPreimages()) {
ClassDependency cls = classCache.getKnown(className);
cls.cleanup();
}
allNodes.clear(); allNodes.clear();
classSource.cleanup(); classSource.cleanup();
agent.cleanup(); agent.cleanup();
listeners.clear(); listeners.clear();
unprocessedClassSource = null; unprocessedClassSource = null;
classSource.innerHierarchy = 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() { public void cleanupTypes() {

View File

@ -41,7 +41,7 @@ class DependencyClassSource implements ClassHolderSource {
private IncrementalDependencyRegistration dependencyRegistration; private IncrementalDependencyRegistration dependencyRegistration;
private Map<String, ClassHolder> generatedClasses = new LinkedHashMap<>(); private Map<String, ClassHolder> generatedClasses = new LinkedHashMap<>();
private List<ClassHolderTransformer> transformers = new ArrayList<>(); private List<ClassHolderTransformer> transformers = new ArrayList<>();
private Map<String, Optional<ClassHolder>> cache = new LinkedHashMap<>(1000, 0.5f); Map<String, Optional<ClassHolder>> cache = new LinkedHashMap<>(1000, 0.5f);
DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics, DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics,
IncrementalDependencyRegistration dependencyRegistration) { IncrementalDependencyRegistration dependencyRegistration) {
@ -93,6 +93,10 @@ class DependencyClassSource implements ClassHolderSource {
return generatedClasses.get(name); return generatedClasses.get(name);
} }
Collection<String> getGeneratedClassNames() {
return generatedClasses.keySet();
}
public Collection<ClassHolder> getGeneratedClasses() { public Collection<ClassHolder> getGeneratedClasses() {
return generatedClasses.values(); return generatedClasses.values();
} }

View File

@ -191,9 +191,9 @@ public class FastDependencyAnalyzer extends DependencyAnalyzer {
} }
@Override @Override
public void cleanup() { public void cleanup(ClassSourcePacker classSourcePacker) {
virtualCallConsumers.clear(); virtualCallConsumers.clear();
subtypeNodes.clear(); subtypeNodes.clear();
super.cleanup(); super.cleanup(classSourcePacker);
} }
} }

View File

@ -26,6 +26,7 @@ import org.teavm.model.FieldReference;
public class FieldDependency implements FieldDependencyInfo { public class FieldDependency implements FieldDependencyInfo {
DependencyNode value; DependencyNode value;
private FieldReader field; private FieldReader field;
private boolean present;
private FieldReference reference; private FieldReference reference;
List<LocationListener> locationListeners; List<LocationListener> locationListeners;
Set<CallLocation> locations; Set<CallLocation> locations;
@ -53,7 +54,7 @@ public class FieldDependency implements FieldDependencyInfo {
@Override @Override
public boolean isMissing() { public boolean isMissing() {
return field == null; return field == null && !present;
} }
public FieldDependency addLocation(CallLocation location) { public FieldDependency addLocation(CallLocation location) {
@ -83,4 +84,11 @@ public class FieldDependency implements FieldDependencyInfo {
} }
} }
} }
void cleanup() {
if (field != null) {
field = null;
present = true;
}
}
} }

View File

@ -32,6 +32,7 @@ public class MethodDependency implements MethodDependencyInfo {
DependencyNode resultNode; DependencyNode resultNode;
DependencyNode thrown; DependencyNode thrown;
MethodHolder method; MethodHolder method;
boolean present;
private MethodReference reference; private MethodReference reference;
boolean used; boolean used;
boolean external; boolean external;
@ -101,7 +102,7 @@ public class MethodDependency implements MethodDependencyInfo {
@Override @Override
public boolean isMissing() { public boolean isMissing() {
return method == null; return method == null && !present;
} }
@Override @Override
@ -179,4 +180,12 @@ public class MethodDependency implements MethodDependencyInfo {
public boolean isCalled() { public boolean isCalled() {
return external; return external;
} }
void cleanup() {
if (method != null) {
present = true;
method = null;
}
}
} }

View File

@ -40,6 +40,7 @@ import org.teavm.cache.EmptyProgramCache;
import org.teavm.cache.ProgramDependencyExtractor; import org.teavm.cache.ProgramDependencyExtractor;
import org.teavm.common.ServiceRepository; import org.teavm.common.ServiceRepository;
import org.teavm.dependency.BootstrapMethodSubstitutor; import org.teavm.dependency.BootstrapMethodSubstitutor;
import org.teavm.dependency.ClassSourcePacker;
import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyInfo;
import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyListener;
@ -152,10 +153,12 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
private int compileProgressReportLimit; private int compileProgressReportLimit;
private int compileProgressLimit; private int compileProgressLimit;
private int compileProgressValue; private int compileProgressValue;
private ClassSourcePacker classSourcePacker;
TeaVM(TeaVMBuilder builder) { TeaVM(TeaVMBuilder builder) {
target = builder.target; target = builder.target;
classLoader = builder.classLoader; classLoader = builder.classLoader;
classSourcePacker = builder.classSourcePacker;
dependencyAnalyzer = builder.dependencyAnalyzerFactory.create(builder.classSource, classLoader, dependencyAnalyzer = builder.dependencyAnalyzerFactory.create(builder.classSource, classLoader,
this, diagnostics, builder.referenceCache); this, diagnostics, builder.referenceCache);
progressListener = new TeaVMProgressListener() { 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. * @param outputName name of output file within buildTarget. Should not be null.
*/ */
public void build(BuildTarget buildTarget, String outputName) { public void build(BuildTarget buildTarget, String outputName) {
target.setController(targetController);
// Check dependencies // Check dependencies
reportPhase(TeaVMPhase.DEPENDENCY_ANALYSIS, lastKnownClasses > 0 ? lastKnownClasses : 1); reportPhase(TeaVMPhase.DEPENDENCY_ANALYSIS, lastKnownClasses > 0 ? lastKnownClasses : 1);
if (wasCancelled()) { if (wasCancelled()) {
@ -368,11 +369,12 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
return; return;
} }
dependencyAnalyzer.setInterruptor(null);
dependencyAnalyzer.cleanup(classSourcePacker);
cacheStatus = new AnnotationAwareCacheStatus(rawCacheStatus, dependencyAnalyzer.getIncrementalDependencies(), cacheStatus = new AnnotationAwareCacheStatus(rawCacheStatus, dependencyAnalyzer.getIncrementalDependencies(),
dependencyAnalyzer.getClassSource()); dependencyAnalyzer.getClassSource());
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass); cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
dependencyAnalyzer.setInterruptor(null); target.setController(targetController);
dependencyAnalyzer.cleanup();
if (wasCancelled()) { if (wasCancelled()) {
return; return;

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.vm; package org.teavm.vm;
import org.teavm.dependency.ClassSourcePacker;
import org.teavm.dependency.DependencyAnalyzerFactory; import org.teavm.dependency.DependencyAnalyzerFactory;
import org.teavm.dependency.PreciseDependencyAnalyzer; import org.teavm.dependency.PreciseDependencyAnalyzer;
import org.teavm.interop.PlatformMarker; import org.teavm.interop.PlatformMarker;
@ -28,6 +29,7 @@ public class TeaVMBuilder {
ClassLoader classLoader; ClassLoader classLoader;
ReferenceCache referenceCache = new ReferenceCache(); ReferenceCache referenceCache = new ReferenceCache();
DependencyAnalyzerFactory dependencyAnalyzerFactory = PreciseDependencyAnalyzer::new; DependencyAnalyzerFactory dependencyAnalyzerFactory = PreciseDependencyAnalyzer::new;
ClassSourcePacker classSourcePacker = (src, names) -> src;
public TeaVMBuilder(TeaVMTarget target) { public TeaVMBuilder(TeaVMTarget target) {
this.target = target; this.target = target;
@ -67,6 +69,11 @@ public class TeaVMBuilder {
return this; return this;
} }
public TeaVMBuilder setClassSourcePacker(ClassSourcePacker classSourcePacker) {
this.classSourcePacker = classSourcePacker;
return this;
}
public TeaVM build() { public TeaVM build() {
return new TeaVM(this); return new TeaVM(this);
} }

View File

@ -292,7 +292,7 @@ public class IncrementalTest {
public boolean isStaleClass(String className) { public boolean isStaleClass(String className) {
ClassReader cls = underlying.get(className); ClassReader cls = underlying.get(className);
if (cls == null) { if (cls == null) {
return true; return false;
} }
return cls.getAnnotations().get(Update.class.getName()) != null; return cls.getAnnotations().get(Update.class.getName()) != null;

View File

@ -38,7 +38,7 @@ import org.teavm.backend.wasm.WasmTarget;
import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.cache.AlwaysStaleCacheStatus; import org.teavm.cache.AlwaysStaleCacheStatus;
import org.teavm.cache.CacheStatus; import org.teavm.cache.CacheStatus;
import org.teavm.cache.DiskCachedClassHolderSource; import org.teavm.cache.DiskCachedClassReaderSource;
import org.teavm.cache.DiskMethodNodeCache; import org.teavm.cache.DiskMethodNodeCache;
import org.teavm.cache.DiskProgramCache; import org.teavm.cache.DiskProgramCache;
import org.teavm.cache.EmptyProgramCache; import org.teavm.cache.EmptyProgramCache;
@ -83,7 +83,7 @@ public class TeaVMTool {
private List<String> classesToPreserve = new ArrayList<>(); private List<String> classesToPreserve = new ArrayList<>();
private TeaVMToolLog log = new EmptyTeaVMToolLog(); private TeaVMToolLog log = new EmptyTeaVMToolLog();
private ClassLoader classLoader = TeaVMTool.class.getClassLoader(); private ClassLoader classLoader = TeaVMTool.class.getClassLoader();
private DiskCachedClassHolderSource cachedClassSource; private DiskCachedClassReaderSource cachedClassSource;
private DiskProgramCache programCache; private DiskProgramCache programCache;
private DiskMethodNodeCache astCache; private DiskMethodNodeCache astCache;
private FileSymbolTable symbolTable; private FileSymbolTable symbolTable;
@ -340,7 +340,7 @@ public class TeaVMTool {
ClasspathClassHolderSource innerClassSource = new ClasspathClassHolderSource(classLoader, ClasspathClassHolderSource innerClassSource = new ClasspathClassHolderSource(classLoader,
referenceCache); referenceCache);
ClassHolderSource classSource = new PreOptimizingClassHolderSource(innerClassSource); ClassHolderSource classSource = new PreOptimizingClassHolderSource(innerClassSource);
cachedClassSource = new DiskCachedClassHolderSource(cacheDirectory, referenceCache, symbolTable, cachedClassSource = new DiskCachedClassReaderSource(cacheDirectory, referenceCache, symbolTable,
fileTable, variableTable, classSource, innerClassSource); fileTable, variableTable, classSource, innerClassSource);
programCache = new DiskProgramCache(cacheDirectory, referenceCache, symbolTable, fileTable, programCache = new DiskProgramCache(cacheDirectory, referenceCache, symbolTable, fileTable,
variableTable); variableTable);

View File

@ -77,6 +77,7 @@ import org.teavm.debugging.information.DebugInformationBuilder;
import org.teavm.dependency.FastDependencyAnalyzer; import org.teavm.dependency.FastDependencyAnalyzer;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.PreOptimizingClassHolderSource; import org.teavm.model.PreOptimizingClassHolderSource;
import org.teavm.model.ReferenceCache; import org.teavm.model.ReferenceCache;
import org.teavm.parsing.ClasspathResourceMapper; import org.teavm.parsing.ClasspathResourceMapper;
@ -713,12 +714,16 @@ public class CodeServlet extends HttpServlet {
private void initBuilder() throws IOException { private void initBuilder() throws IOException {
watcher = new FileSystemWatcher(classPath); watcher = new FileSystemWatcher(classPath);
classSource = new MemoryCachedClassReaderSource(referenceCache, symbolTable, fileSymbolTable, classSource = createCachedSource();
variableSymbolTable);
astCache = new InMemoryMethodNodeCache(referenceCache, symbolTable, fileSymbolTable, variableSymbolTable); astCache = new InMemoryMethodNodeCache(referenceCache, symbolTable, fileSymbolTable, variableSymbolTable);
programCache = new InMemoryProgramCache(referenceCache, symbolTable, fileSymbolTable, variableSymbolTable); programCache = new InMemoryProgramCache(referenceCache, symbolTable, fileSymbolTable, variableSymbolTable);
} }
private MemoryCachedClassReaderSource createCachedSource() {
return new MemoryCachedClassReaderSource(referenceCache, symbolTable, fileSymbolTable,
variableSymbolTable);
}
private void shutdownBuilder() { private void shutdownBuilder() {
try { try {
watcher.dispose(); watcher.dispose();
@ -757,6 +762,7 @@ public class CodeServlet extends HttpServlet {
.setClassLoader(classLoader) .setClassLoader(classLoader)
.setClassSource(classSource) .setClassSource(classSource)
.setDependencyAnalyzerFactory(FastDependencyAnalyzer::new) .setDependencyAnalyzerFactory(FastDependencyAnalyzer::new)
.setClassSourcePacker(this::packClasses)
.build(); .build();
jsTarget.setStackTraceIncluded(true); jsTarget.setStackTraceIncluded(true);
@ -784,6 +790,16 @@ public class CodeServlet extends HttpServlet {
postBuild(vm, startTime); postBuild(vm, startTime);
} }
private ClassReaderSource packClasses(ClassReaderSource source, Collection<? extends String> classNames) {
MemoryCachedClassReaderSource packedSource = createCachedSource();
packedSource.setProvider(source::get);
for (String className : classNames) {
packedSource.populate(className);
}
packedSource.setProvider(null);
return packedSource;
}
private void addIndicator() { private void addIndicator() {
String script = getIndicatorScript(false); String script = getIndicatorScript(false);
try (Writer writer = new OutputStreamWriter(buildTarget.appendToResource(fileName), StandardCharsets.UTF_8)) { try (Writer writer = new OutputStreamWriter(buildTarget.appendToResource(fileName), StandardCharsets.UTF_8)) {