mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C: incremental code generator
This commit is contained in:
parent
c1d4ed2e3c
commit
054db3e8d1
|
@ -18,14 +18,12 @@ package org.teavm.backend.c;
|
|||
import com.carrotsearch.hppc.ObjectByteHashMap;
|
||||
import com.carrotsearch.hppc.ObjectByteMap;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
|
@ -48,6 +46,7 @@ import org.teavm.backend.c.generate.IncludeManager;
|
|||
import org.teavm.backend.c.generate.NameProvider;
|
||||
import org.teavm.backend.c.generate.OutputFileUtil;
|
||||
import org.teavm.backend.c.generate.SimpleIncludeManager;
|
||||
import org.teavm.backend.c.generate.SimpleStringPool;
|
||||
import org.teavm.backend.c.generate.StringPool;
|
||||
import org.teavm.backend.c.generate.StringPoolGenerator;
|
||||
import org.teavm.backend.c.generators.ArrayGenerator;
|
||||
|
@ -102,6 +101,7 @@ import org.teavm.model.classes.VirtualTableProvider;
|
|||
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.Characteristics;
|
||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||
|
@ -141,12 +141,27 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
private int minHeapSize = 32 * 1024 * 1024;
|
||||
private List<IntrinsicFactory> intrinsicFactories = new ArrayList<>();
|
||||
private List<GeneratorFactory> generatorFactories = new ArrayList<>();
|
||||
private Characteristics characteristics;
|
||||
private Set<MethodReference> asyncMethods;
|
||||
private boolean incremental;
|
||||
private StringPool stringPool;
|
||||
|
||||
public CTarget() {
|
||||
this(new SimpleStringPool());
|
||||
}
|
||||
|
||||
public CTarget(StringPool stringPool) {
|
||||
this.stringPool = stringPool;
|
||||
}
|
||||
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
public void setIncremental(boolean incremental) {
|
||||
this.incremental = incremental;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClassHolderTransformer> getTransformers() {
|
||||
List<ClassHolderTransformer> transformers = new ArrayList<>();
|
||||
|
@ -163,7 +178,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
@Override
|
||||
public void setController(TeaVMTargetController controller) {
|
||||
this.controller = controller;
|
||||
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||
characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||
classInitializerTransformer = new ClassInitializerTransformer();
|
||||
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
||||
|
@ -273,6 +288,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
nullCheckTransformation.apply(program, method.getResultType());
|
||||
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods)
|
||||
.apply(program, method.getReference());
|
||||
ShadowStackTransformer shadowStackTransformer = !incremental
|
||||
? this.shadowStackTransformer
|
||||
: new ShadowStackTransformer(characteristics);
|
||||
shadowStackTransformer.apply(program, method);
|
||||
}
|
||||
|
||||
|
@ -281,7 +299,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
|
||||
ClassHierarchy hierarchy = new ClassHierarchy(classes);
|
||||
TagRegistry tagRegistry = new TagRegistry(classes, hierarchy);
|
||||
StringPool stringPool = new StringPool();
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true);
|
||||
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||
|
@ -313,12 +330,18 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
|
||||
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
|
||||
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
|
||||
intrinsics, generators, asyncMethods::contains, buildTarget);
|
||||
intrinsics, generators, asyncMethods::contains, buildTarget, incremental);
|
||||
|
||||
BufferedCodeWriter runtimeWriter = new BufferedCodeWriter();
|
||||
copyResource("runtime.h", "runtime.h", buildTarget);
|
||||
BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter();
|
||||
emitResource(runtimeWriter, "runtime.c");
|
||||
|
||||
runtimeHeaderWriter.println("#pragma once");
|
||||
if (incremental) {
|
||||
runtimeHeaderWriter.println("#define TEAVM_INCREMENTAL true");
|
||||
}
|
||||
emitResource(runtimeHeaderWriter, "runtime.h");
|
||||
|
||||
ClassGenerator classGenerator = new ClassGenerator(context, controller.getUnprocessedClassSource(),
|
||||
tagRegistry, decompiler);
|
||||
IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl(
|
||||
|
@ -334,8 +357,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
generateClasses(classes, classGenerator, buildTarget);
|
||||
generateSpecialFunctions(context, runtimeWriter);
|
||||
OutputFileUtil.write(runtimeWriter, "runtime.c", buildTarget);
|
||||
OutputFileUtil.write(runtimeHeaderWriter, "runtime.h", buildTarget);
|
||||
|
||||
generateCallSites(buildTarget, context);
|
||||
generateCallSites(buildTarget, context, classes.getClassNames());
|
||||
generateStrings(buildTarget, stringPool);
|
||||
|
||||
List<ValueType> types = classGenerator.getTypes().stream()
|
||||
|
@ -362,25 +386,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
}
|
||||
}
|
||||
|
||||
private void copyResource(String resourceName, String targetName, BuildTarget buildTarget) {
|
||||
ClassLoader classLoader = CTarget.class.getClassLoader();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
classLoader.getResourceAsStream("org/teavm/backend/c/" + resourceName)));
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
|
||||
buildTarget.createResource(targetName), StandardCharsets.UTF_8))) {
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
writer.write(line);
|
||||
writer.write('\n');
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateClasses(ListableClassHolderSource classes, ClassGenerator classGenerator,
|
||||
BuildTarget buildTarget) throws IOException {
|
||||
List<String> classNames = sortClassNames(classes);
|
||||
|
@ -444,7 +449,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
return classNames;
|
||||
}
|
||||
|
||||
private void generateCallSites(BuildTarget buildTarget, GenerationContext context) throws IOException {
|
||||
private void generateCallSites(BuildTarget buildTarget, GenerationContext context,
|
||||
Collection<? extends String> classNames) throws IOException {
|
||||
BufferedCodeWriter writer = new BufferedCodeWriter();
|
||||
BufferedCodeWriter headerWriter = new BufferedCodeWriter();
|
||||
|
||||
|
@ -458,15 +464,32 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
headerIncludes.includePath("runtime.h");
|
||||
headerIncludes.includeClass(CallSiteGenerator.CALL_SITE);
|
||||
|
||||
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
|
||||
headerWriter.println("extern " + callSiteName + " teavm_callSites[];");
|
||||
|
||||
new CallSiteGenerator(context, writer, includes).generate(shadowStackTransformer.getCallSites());
|
||||
if (incremental) {
|
||||
generateIncrementalCallSites(context, headerWriter);
|
||||
} else {
|
||||
generateFastCallSites(context, writer, includes, headerWriter, classNames);
|
||||
}
|
||||
|
||||
OutputFileUtil.write(writer, "callsites.c", buildTarget);
|
||||
OutputFileUtil.write(headerWriter, "callsites.h", buildTarget);
|
||||
}
|
||||
|
||||
private void generateFastCallSites(GenerationContext context, CodeWriter writer, IncludeManager includes,
|
||||
CodeWriter headerWriter, Collection<? extends String> classNames) {
|
||||
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
|
||||
headerWriter.println("extern " + callSiteName + " teavm_callSites[];");
|
||||
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) (teavm_callSites + id)");
|
||||
|
||||
new CallSiteGenerator(context, writer, includes, "teavm_callSites")
|
||||
.generate(CallSiteDescriptor.extract(context.getClassSource(), classNames));
|
||||
}
|
||||
|
||||
private void generateIncrementalCallSites(GenerationContext context, CodeWriter headerWriter) {
|
||||
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
|
||||
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) (((" + callSiteName
|
||||
+ "*) ((void**) frame)[3]) + id)");
|
||||
}
|
||||
|
||||
private void generateStrings(BuildTarget buildTarget, StringPool stringPool) throws IOException {
|
||||
BufferedCodeWriter writer = new BufferedCodeWriter();
|
||||
BufferedCodeWriter headerWriter = new BufferedCodeWriter();
|
||||
|
@ -685,7 +708,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
|
||||
int size = context.getStringPool().getStrings().size();
|
||||
writer.println("for (int i = 0; i < " + size + "; ++i) {").indent();
|
||||
writer.println("((TeaVM_Object*) (teavm_stringPool + i))->header = stringHeader;");
|
||||
writer.println("TeaVM_Object *s = (TeaVM_Object*) (teavm_stringPool + i);");
|
||||
writer.println("if (s != NULL) s->header = stringHeader;");
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
|
||||
|
|
|
@ -38,16 +38,24 @@ public class CallSiteGenerator {
|
|||
private List<ExceptionHandlerDescriptor> exceptionHandlers = new ArrayList<>();
|
||||
private String callSiteLocationName;
|
||||
private String exceptionHandlerName;
|
||||
private String callSitesName;
|
||||
private boolean isStatic;
|
||||
|
||||
public CallSiteGenerator(GenerationContext context, CodeWriter writer, IncludeManager includes) {
|
||||
public CallSiteGenerator(GenerationContext context, CodeWriter writer, IncludeManager includes,
|
||||
String callSitesName) {
|
||||
this.context = context;
|
||||
this.writer = writer;
|
||||
this.includes = includes;
|
||||
callSiteLocationName = context.getNames().forClass(CALL_SITE_LOCATION);
|
||||
exceptionHandlerName = context.getNames().forClass(EXCEPTION_HANDLER);
|
||||
this.callSitesName = callSitesName;
|
||||
}
|
||||
|
||||
public void generate(List<CallSiteDescriptor> callSites) {
|
||||
public void setStatic(boolean isStatic) {
|
||||
this.isStatic = isStatic;
|
||||
}
|
||||
|
||||
public void generate(List<? extends CallSiteDescriptor> callSites) {
|
||||
CodeWriter writerForLocations = writer.fragment();
|
||||
generateCallSites(callSites);
|
||||
|
||||
|
@ -58,12 +66,15 @@ public class CallSiteGenerator {
|
|||
writer = oldWriter;
|
||||
}
|
||||
|
||||
private void generateCallSites(List<CallSiteDescriptor> callSites) {
|
||||
private void generateCallSites(List<? extends CallSiteDescriptor> callSites) {
|
||||
String callSiteName = context.getNames().forClass(CALL_SITE);
|
||||
|
||||
includes.includeClass(CALL_SITE);
|
||||
includes.includePath("strings.h");
|
||||
writer.print(callSiteName).print(" teavm_callSites[" + callSites.size() + "] = {").indent();
|
||||
if (isStatic) {
|
||||
writer.print("static ");
|
||||
}
|
||||
writer.print(callSiteName).print(" " + callSitesName + "[" + callSites.size() + "] = {").indent();
|
||||
String handlerCountName = fieldName(CALL_SITE, "handlerCount");
|
||||
String firstHandlerName = fieldName(CALL_SITE, "firstHandler");
|
||||
String locationName = fieldName(CALL_SITE, "location");
|
||||
|
@ -86,14 +97,14 @@ public class CallSiteGenerator {
|
|||
}
|
||||
|
||||
String firstHandlerExpr = !callSite.getHandlers().isEmpty()
|
||||
? "teavm_exceptionHandlers + " + exceptionHandlers.size()
|
||||
? "exceptionHandlers_" + callSitesName + " + " + exceptionHandlers.size()
|
||||
: "NULL";
|
||||
writer.println().print("{ ");
|
||||
writer.print(".").print(handlerCountName).print(" = ")
|
||||
.print(String.valueOf(callSite.getHandlers().size())).print(", ");
|
||||
writer.print(".").print(firstHandlerName).print(" = ").print(firstHandlerExpr).print(", ");
|
||||
writer.print(".").print(locationName).print(" = ")
|
||||
.print(locationIndex >= 0 ? "teavm_callSiteLocations + " + locationIndex : "NULL");
|
||||
.print(locationIndex >= 0 ? "callSiteLocations_" + callSitesName + " + " + locationIndex : "NULL");
|
||||
writer.print(" }");
|
||||
|
||||
exceptionHandlers.addAll(callSite.getHandlers());
|
||||
|
@ -104,8 +115,8 @@ public class CallSiteGenerator {
|
|||
|
||||
private void generateLocations() {
|
||||
includes.includeClass(CALL_SITE_LOCATION);
|
||||
writer.print("static ").print(callSiteLocationName).print(" teavm_callSiteLocations[" + locations.size()
|
||||
+ "] = {").indent();
|
||||
writer.print("static ").print(callSiteLocationName).print(" callSiteLocations_" + callSitesName
|
||||
+ "[" + locations.size() + "] = {").indent();
|
||||
|
||||
String fileNameName = fieldName(CALL_SITE_LOCATION, "fileName");
|
||||
String classNameName = fieldName(CALL_SITE_LOCATION, "className");
|
||||
|
@ -137,7 +148,7 @@ public class CallSiteGenerator {
|
|||
|
||||
private void generateHandlers() {
|
||||
includes.includeClass(EXCEPTION_HANDLER);
|
||||
writer.print("static ").print(exceptionHandlerName).print(" teavm_exceptionHandlers["
|
||||
writer.print("static ").print(exceptionHandlerName).print(" exceptionHandlers_" + callSitesName + "["
|
||||
+ exceptionHandlers.size() + "] = {").indent();
|
||||
|
||||
String idName = fieldName(EXCEPTION_HANDLER, "id");
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.backend.c.generate;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
@ -57,7 +58,9 @@ import org.teavm.model.instructions.ConstructInstruction;
|
|||
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||
import org.teavm.model.instructions.InstructionVisitor;
|
||||
import org.teavm.model.instructions.StringConstantInstruction;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.Characteristics;
|
||||
import org.teavm.runtime.CallSite;
|
||||
import org.teavm.runtime.RuntimeArray;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.runtime.RuntimeObject;
|
||||
|
@ -81,6 +84,7 @@ public class ClassGenerator {
|
|||
private CodeWriter headerWriter;
|
||||
private IncludeManager includes;
|
||||
private IncludeManager headerIncludes;
|
||||
private boolean stackDefined;
|
||||
|
||||
public ClassGenerator(GenerationContext context, ClassReaderSource unprocessedClassSource,
|
||||
TagRegistry tagRegistry, Decompiler decompiler) {
|
||||
|
@ -161,7 +165,9 @@ public class ClassGenerator {
|
|||
};
|
||||
|
||||
public void generateClass(CodeWriter writer, CodeWriter headerWriter, ClassHolder cls) {
|
||||
stackDefined = false;
|
||||
init(writer, headerWriter, fileName(cls.getName()));
|
||||
|
||||
codeGenerator = new CodeGenerator(context, codeWriter, includes);
|
||||
|
||||
String sysInitializerName = context.getNames().forClassSystemInitializer(cls.getName());
|
||||
|
@ -180,6 +186,12 @@ public class ClassGenerator {
|
|||
generateLayoutArray(cls.getName());
|
||||
}
|
||||
|
||||
private void generateCallSites(List<? extends CallSiteDescriptor> callSites, String callSitesName) {
|
||||
CallSiteGenerator generator = new CallSiteGenerator(context, codeWriter, includes, callSitesName);
|
||||
generator.setStatic(true);
|
||||
generator.generate(callSites);
|
||||
}
|
||||
|
||||
public void generateType(CodeWriter writer, CodeWriter headerWriter, ValueType type) {
|
||||
init(writer, headerWriter, fileName(type));
|
||||
includes.includeType(type);
|
||||
|
@ -220,6 +232,24 @@ public class ClassGenerator {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (context.isIncremental()) {
|
||||
String callSitesName;
|
||||
List<? extends CallSiteDescriptor> callSites = CallSiteDescriptor.extract(method.getProgram());
|
||||
if (!callSites.isEmpty()) {
|
||||
callSitesName = "callsites_" + context.getNames().forMethod(method.getReference());
|
||||
includes.includeClass(CallSite.class.getName());
|
||||
generateCallSites(callSites, callSitesName);
|
||||
} else {
|
||||
callSitesName = "NULL";
|
||||
}
|
||||
if (stackDefined) {
|
||||
codeWriter.println("#undef TEAVM_ALLOC_STACK");
|
||||
}
|
||||
codeWriter.println("#define TEAVM_ALLOC_STACK(size) TEAVM_ALLOC_STACK_DEF(size, "
|
||||
+ callSitesName + ")");
|
||||
stackDefined = true;
|
||||
}
|
||||
|
||||
generateMethodForwardDeclaration(method);
|
||||
RegularMethodNode methodNode = decompiler.decompileRegular(method);
|
||||
codeGenerator.generateMethod(methodNode);
|
||||
|
@ -454,6 +484,8 @@ public class ClassGenerator {
|
|||
int flags = 0;
|
||||
String layout = "NULL";
|
||||
String initFunction = "NULL";
|
||||
String superinterfaceCount = "0";
|
||||
String superinterfaces = "NULL";
|
||||
|
||||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
|
@ -473,7 +505,7 @@ public class ClassGenerator {
|
|||
flags |= RuntimeClass.ENUM;
|
||||
}
|
||||
List<TagRegistry.Range> ranges = tagRegistry.getRanges(className);
|
||||
tag = ranges != null && !ranges.isEmpty() ? ranges.get(0).lower : 0;
|
||||
tag = !context.isIncremental() && ranges != null && !ranges.isEmpty() ? ranges.get(0).lower : 0;
|
||||
|
||||
if (cls != null && cls.getParent() != null && types.contains(ValueType.object(cls.getParent()))) {
|
||||
includes.includeClass(cls.getParent());
|
||||
|
@ -487,10 +519,31 @@ public class ClassGenerator {
|
|||
if (cls != null && needsInitializer(cls)) {
|
||||
initFunction = context.getNames().forClassInitializer(className);
|
||||
}
|
||||
|
||||
Set<String> interfaces = cls != null
|
||||
? cls.getInterfaces().stream()
|
||||
.filter(c -> types.contains(ValueType.object(c)))
|
||||
.collect(Collectors.toSet())
|
||||
: Collections.emptySet();
|
||||
if (!interfaces.isEmpty()) {
|
||||
superinterfaceCount = Integer.toString(cls.getInterfaces().size());
|
||||
StringBuilder sb = new StringBuilder("(TeaVM_Class*[]) { ");
|
||||
boolean first = true;
|
||||
for (String itf : interfaces) {
|
||||
if (!first) {
|
||||
sb.append(", ");
|
||||
}
|
||||
first = false;
|
||||
includes.includeClass(itf);
|
||||
sb.append("(TeaVM_Class*) &").append(context.getNames().forClassInstance(ValueType.object(itf)));
|
||||
}
|
||||
superinterfaces = sb.append(" }").toString();
|
||||
}
|
||||
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
includes.includeClass("java.lang.Object");
|
||||
parent = "(TeaVM_Class*) &" + context.getNames().forClassInstance(ValueType.object("java.lang.Object"));
|
||||
tag = tagRegistry.getRanges("java.lang.Object").get(0).lower;
|
||||
tag = !context.isIncremental() ? tagRegistry.getRanges("java.lang.Object").get(0).lower : 0;
|
||||
ValueType itemType = ((ValueType.Array) type).getItemType();
|
||||
sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(itemType) + ")";
|
||||
includes.includeType(itemType);
|
||||
|
@ -532,6 +585,8 @@ public class ClassGenerator {
|
|||
codeWriter.println(".itemType = " + itemTypeExpr + ",");
|
||||
codeWriter.println(".isSupertypeOf = &" + superTypeFunction + ",");
|
||||
codeWriter.println(".superclass = " + parent + ",");
|
||||
codeWriter.println(".superinterfaceCount = " + superinterfaceCount + ",");
|
||||
codeWriter.println(".superinterfaces = " + superinterfaces + ",");
|
||||
codeWriter.println(".enumValues = NULL,");
|
||||
codeWriter.println(".layout = " + layout + ",");
|
||||
codeWriter.println(".enumValues = " + enumConstants + ",");
|
||||
|
@ -566,6 +621,7 @@ public class ClassGenerator {
|
|||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
return !context.getCharacteristics().isStructure(className)
|
||||
&& !context.getCharacteristics().isFunction(className)
|
||||
&& !className.equals(Address.class.getName());
|
||||
} else {
|
||||
return type instanceof ValueType.Array;
|
||||
|
@ -766,6 +822,14 @@ public class ClassGenerator {
|
|||
}
|
||||
|
||||
private void generateIsSuperclassFunction(String className) {
|
||||
if (context.isIncremental()) {
|
||||
generateIncrementalSuperclassFunction(className);
|
||||
} else {
|
||||
generateFastIsSuperclassFunction(className);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateFastIsSuperclassFunction(String className) {
|
||||
List<TagRegistry.Range> ranges = tagRegistry.getRanges(className);
|
||||
if (ranges.isEmpty()) {
|
||||
codeWriter.println("return INT32_C(0);");
|
||||
|
@ -789,6 +853,25 @@ public class ClassGenerator {
|
|||
codeWriter.println("return INT32_C(1);");
|
||||
}
|
||||
|
||||
private void generateIncrementalSuperclassFunction(String className) {
|
||||
String functionName = context.getNames().forSupertypeFunction(ValueType.object(className));
|
||||
ClassReader cls = context.getClassSource().get(className);
|
||||
if (cls != null && types.contains(ValueType.object(className))) {
|
||||
includes.includeClass(className);
|
||||
String name = context.getNames().forClassInstance(ValueType.object(className));
|
||||
codeWriter.println("if (cls == (TeaVM_Class*) &" + name + ") return INT32_C(1);");
|
||||
|
||||
codeWriter.println("if (cls->superclass != NULL && " + functionName + "(cls->superclass)) "
|
||||
+ "return INT32_C(1);");
|
||||
codeWriter.println("for (int32_t i = 0; i < cls->superinterfaceCount; ++i) {").indent();
|
||||
codeWriter.println("if (" + functionName + "(cls->superinterfaces[i])) "
|
||||
+ "return INT32_C(1);");
|
||||
codeWriter.outdent().println("}");
|
||||
}
|
||||
|
||||
codeWriter.println("return INT32_C(0);");
|
||||
}
|
||||
|
||||
private void generateIsSuperArrayFunction(ValueType itemType) {
|
||||
String itemTypeName = context.getNames().forMemberField(new FieldReference(
|
||||
RuntimeClass.class.getName(), "itemType"));
|
||||
|
|
|
@ -517,6 +517,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
writer.print("TEAVM_ARRAY_DATA(TEAVM_FIELD(");
|
||||
String typeName = ((ValueType.Object) type).getClassName();
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
includes.includeClass(typeName);
|
||||
writer.print(", ").print(names.forClass(typeName)).print(", ")
|
||||
.print(names.forMemberField(new FieldReference(typeName, "array"))).print(")");
|
||||
writer.print(", ").print(BUFFER_TYPES.get(typeName)).print(")");
|
||||
|
@ -1016,6 +1017,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
ClassGenerator.escape(name, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIncremental() {
|
||||
return context.isIncremental();
|
||||
}
|
||||
};
|
||||
|
||||
private static CVariableType typeToCType(ValueType type) {
|
||||
|
|
|
@ -43,11 +43,12 @@ public class GenerationContext {
|
|||
private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>();
|
||||
private Predicate<MethodReference> asyncMethods;
|
||||
private BuildTarget buildTarget;
|
||||
private boolean incremental;
|
||||
|
||||
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
|
||||
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
|
||||
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators,
|
||||
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget) {
|
||||
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget, boolean incremental) {
|
||||
this.virtualTableProvider = virtualTableProvider;
|
||||
this.characteristics = characteristics;
|
||||
this.dependencies = dependencies;
|
||||
|
@ -59,6 +60,7 @@ public class GenerationContext {
|
|||
this.generators = new ArrayList<>(generators);
|
||||
this.asyncMethods = asyncMethods;
|
||||
this.buildTarget = buildTarget;
|
||||
this.incremental = incremental;
|
||||
}
|
||||
|
||||
public void addIntrinsic(Intrinsic intrinsic) {
|
||||
|
@ -118,4 +120,8 @@ public class GenerationContext {
|
|||
public BuildTarget getBuildTarget() {
|
||||
return buildTarget;
|
||||
}
|
||||
|
||||
public boolean isIncremental() {
|
||||
return incremental;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ public class NameProvider extends LowLevelNameProvider {
|
|||
memberFieldNames.put(new FieldReference(String.class.getName(), "hashCode"), "hashCode");
|
||||
|
||||
for (String name : new String[] { "size", "flags", "tag", "canary", "name", "itemType", "arrayType",
|
||||
"isSupertypeOf", "init", "enumValues", "layout", "simpleName" }) {
|
||||
"isSupertypeOf", "init", "enumValues", "layout", "simpleName", "superinterfaceCount",
|
||||
"superinterfaces" }) {
|
||||
memberFieldNames.put(new FieldReference(RuntimeClass.class.getName(), name), name);
|
||||
}
|
||||
memberFieldNames.put(new FieldReference(RuntimeClass.class.getName(), "parent"), "superclass");
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.c.generate;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SimpleStringPool implements StringPool {
|
||||
private ObjectIntMap<String> stringIndexes = new ObjectIntHashMap<>();
|
||||
private final List<String> strings = new ArrayList<>();
|
||||
private final List<String> readonlyStrings = Collections.unmodifiableList(strings);
|
||||
private final List<FreeIndex> freeIndexes = new ArrayList<>();
|
||||
private FreeIndex firstFreeIndex;
|
||||
private FreeIndex lastFreeIndex;
|
||||
private ObjectIntMap<String> lastStringIndexes = new ObjectIntHashMap<>();
|
||||
|
||||
@Override
|
||||
public int getStringIndex(String string) {
|
||||
int index = stringIndexes.getOrDefault(string, -1);
|
||||
if (index < 0) {
|
||||
index = lastStringIndexes.getOrDefault(string, -1);
|
||||
if (index >= 0) {
|
||||
removeFreeIndex(index);
|
||||
strings.set(index, string);
|
||||
} else if (firstFreeIndex != null) {
|
||||
index = firstFreeIndex.value;
|
||||
freeIndexes.set(index, null);
|
||||
firstFreeIndex = firstFreeIndex.next;
|
||||
if (firstFreeIndex == null) {
|
||||
lastFreeIndex = null;
|
||||
}
|
||||
strings.set(index, string);
|
||||
} else {
|
||||
index = strings.size();
|
||||
strings.add(string);
|
||||
}
|
||||
stringIndexes.put(string, index);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private void removeFreeIndex(int index) {
|
||||
FreeIndex freeIndex = freeIndexes.get(index);
|
||||
if (freeIndex == null) {
|
||||
return;
|
||||
}
|
||||
freeIndexes.set(index, null);
|
||||
|
||||
if (freeIndex.previous != null) {
|
||||
freeIndex.previous.next = freeIndex.next;
|
||||
} else {
|
||||
firstFreeIndex = freeIndex.next;
|
||||
}
|
||||
if (freeIndex.next != null) {
|
||||
freeIndex.next.previous = freeIndex.previous;
|
||||
} else {
|
||||
lastFreeIndex = freeIndex.previous;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getStrings() {
|
||||
return readonlyStrings;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
for (int i = freeIndexes.size(); i < strings.size(); ++i) {
|
||||
FreeIndex freeIndex = new FreeIndex();
|
||||
freeIndexes.add(freeIndex);
|
||||
if (lastFreeIndex != null) {
|
||||
freeIndex.previous = lastFreeIndex;
|
||||
lastFreeIndex.next = freeIndex;
|
||||
}
|
||||
lastFreeIndex = freeIndex;
|
||||
}
|
||||
lastStringIndexes.clear();
|
||||
lastStringIndexes.putAll(stringIndexes);
|
||||
stringIndexes.clear();
|
||||
for (int i = 0; i < strings.size(); ++i) {
|
||||
strings.set(i, null);
|
||||
}
|
||||
}
|
||||
|
||||
static class FreeIndex {
|
||||
int value;
|
||||
FreeIndex previous;
|
||||
FreeIndex next;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -15,28 +15,10 @@
|
|||
*/
|
||||
package org.teavm.backend.c.generate;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class StringPool {
|
||||
private final ObjectIntMap<String> stringIndexes = new ObjectIntHashMap<>();
|
||||
private final List<String> strings = new ArrayList<>();
|
||||
private final List<String> readonlyStrings = Collections.unmodifiableList(strings);
|
||||
public interface StringPool {
|
||||
int getStringIndex(String string);
|
||||
|
||||
public int getStringIndex(String string) {
|
||||
int index = stringIndexes.getOrDefault(string, -1);
|
||||
if (index < 0) {
|
||||
index = strings.size();
|
||||
stringIndexes.put(string, index);
|
||||
strings.add(string);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public List<String> getStrings() {
|
||||
return readonlyStrings;
|
||||
}
|
||||
List<String> getStrings();
|
||||
}
|
||||
|
|
|
@ -28,16 +28,20 @@ public class StringPoolGenerator {
|
|||
writer.println("TeaVM_String teavm_stringPool[" + strings.size() + "] = {").indent();
|
||||
for (int i = 0; i < strings.size(); ++i) {
|
||||
String s = strings.get(i);
|
||||
boolean codes = hasBadCharacters(s);
|
||||
String macroName = codes ? "TEAVM_STRING_FROM_CODES" : "TEAVM_STRING";
|
||||
writer.print(macroName + "(" + s.length() + ", " + s.hashCode() + ",");
|
||||
if (codes) {
|
||||
generateNumericStringLiteral(s);
|
||||
if (s == null) {
|
||||
writer.println("TEAVM_NULL_STRING");
|
||||
} else {
|
||||
writer.print("u");
|
||||
generateSimpleStringLiteral(writer, s);
|
||||
boolean codes = hasBadCharacters(s);
|
||||
String macroName = codes ? "TEAVM_STRING_FROM_CODES" : "TEAVM_STRING";
|
||||
writer.print(macroName + "(" + s.length() + ", " + s.hashCode() + ",");
|
||||
if (codes) {
|
||||
generateNumericStringLiteral(s);
|
||||
} else {
|
||||
writer.print("u");
|
||||
generateSimpleStringLiteral(writer, s);
|
||||
}
|
||||
writer.print(")");
|
||||
}
|
||||
writer.print(")");
|
||||
|
||||
writer.print(i < strings.size() - 1 ? "," : " ");
|
||||
writer.print(" // string #" + i);
|
||||
|
|
|
@ -39,8 +39,10 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
|
|||
switch (invocation.getMethod().getName()) {
|
||||
case "findCallSiteById":
|
||||
context.includes().includePath("callsites.h");
|
||||
context.writer().print("(teavm_callSites + ");
|
||||
context.writer().print("TEAVM_FIND_CALLSITE(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(", ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -39,4 +39,6 @@ public interface IntrinsicContext {
|
|||
IncludeManager includes();
|
||||
|
||||
String escapeFileName(String name);
|
||||
|
||||
boolean isIncremental();
|
||||
}
|
||||
|
|
|
@ -78,15 +78,16 @@ public class ShadowStackIntrinsic implements Intrinsic {
|
|||
context.writer().print(")[0]");
|
||||
return;
|
||||
case "getStackRootCount":
|
||||
context.writer().print("((int32_t) (intptr_t) ((void**) ");
|
||||
context.writer().print("TEAVM_GC_ROOTS_COUNT(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")[2])");
|
||||
context.writer().print(")");
|
||||
return;
|
||||
case "getStackRootPointer":
|
||||
context.writer().print("&((void**) ");
|
||||
case "getStackRootPointer": {
|
||||
context.writer().print("TEAVM_GET_GC_ROOTS(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")[3]");
|
||||
context.writer().print(")");
|
||||
return;
|
||||
}
|
||||
case "getCallSiteId":
|
||||
context.writer().print("((int32_t) (intptr_t) ((void**) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
|
|
|
@ -34,7 +34,7 @@ public class ExceptionHandlingDependencyListener extends AbstractDependencyListe
|
|||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
if (method.getReference().equals(FILL_IN_STACK_TRACE)) {
|
||||
DependencyNode node = agent.linkField(STACK_TRACE).getValue();
|
||||
node.propagate(agent.getType("[java/lang.StackTraceElement;"));
|
||||
node.propagate(agent.getType("[Ljava/lang/StackTraceElement;"));
|
||||
node.getArrayItem().propagate(agent.getType("java.lang.StackTraceElement"));
|
||||
|
||||
MethodDependency initElem = agent.linkMethod(STACK_TRACE_ELEMENT_INIT);
|
||||
|
|
|
@ -127,6 +127,7 @@ import org.teavm.model.classes.VirtualTableProvider;
|
|||
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.Characteristics;
|
||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||
|
@ -374,7 +375,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
int pages = (minHeapSize + pageSize - 1) / pageSize;
|
||||
module.setMemorySize(pages);
|
||||
generateMethods(classes, context, generator, classGenerator, binaryWriter, module);
|
||||
exceptionHandlingIntrinsic.postProcess(shadowStackTransformer.getCallSites());
|
||||
exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames()));
|
||||
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
|
||||
classGenerator.postProcess();
|
||||
mutatorIntrinsic.setStaticGcRootsAddress(classGenerator.getStaticGcRootsAddress());
|
||||
|
|
|
@ -62,7 +62,7 @@ public class CallSiteBinaryGenerator {
|
|||
this.stringPool = stringPool;
|
||||
}
|
||||
|
||||
public int writeCallSites(List<CallSiteDescriptor> callSites) {
|
||||
public int writeCallSites(List<? extends CallSiteDescriptor> callSites) {
|
||||
if (callSites.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
|
|||
return false;
|
||||
}
|
||||
|
||||
public void postProcess(List<CallSiteDescriptor> callSites) {
|
||||
public void postProcess(List<? extends CallSiteDescriptor> callSites) {
|
||||
int address = callSiteBinaryGenerator.writeCallSites(callSites);
|
||||
for (WasmInt32Constant constant : constants) {
|
||||
constant.setValue(address);
|
||||
|
|
177
core/src/main/java/org/teavm/cache/AnnotationIO.java
vendored
Normal file
177
core/src/main/java/org/teavm/cache/AnnotationIO.java
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* 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.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.teavm.model.AnnotationContainerReader;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.ReferenceCache;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class AnnotationIO {
|
||||
private ReferenceCache referenceCache;
|
||||
private SymbolTable symbolTable;
|
||||
|
||||
public AnnotationIO(ReferenceCache referenceCache, SymbolTable symbolTable) {
|
||||
this.referenceCache = referenceCache;
|
||||
this.symbolTable = symbolTable;
|
||||
}
|
||||
|
||||
public void writeAnnotations(VarDataOutput output, AnnotationContainerReader annotations) throws IOException {
|
||||
List<AnnotationReader> annotationList = new ArrayList<>();
|
||||
for (AnnotationReader annot : annotations.all()) {
|
||||
annotationList.add(annot);
|
||||
}
|
||||
output.writeUnsigned(annotationList.size());
|
||||
for (AnnotationReader annot : annotationList) {
|
||||
writeAnnotation(output, annot);
|
||||
}
|
||||
}
|
||||
|
||||
public CachedAnnotations readAnnotations(VarDataInput input) throws IOException {
|
||||
Map<String, CachedAnnotation> annotations = new HashMap<>();
|
||||
int annotCount = input.readUnsigned();
|
||||
for (int i = 0; i < annotCount; ++i) {
|
||||
CachedAnnotation annot = readAnnotation(input);
|
||||
annotations.put(annot.type, annot);
|
||||
}
|
||||
return new CachedAnnotations(annotations);
|
||||
}
|
||||
|
||||
private void writeAnnotation(VarDataOutput output, AnnotationReader annotation) throws IOException {
|
||||
output.writeUnsigned(symbolTable.lookup(annotation.getType()));
|
||||
int fieldCount = 0;
|
||||
for (@SuppressWarnings("unused") String field : annotation.getAvailableFields()) {
|
||||
++fieldCount;
|
||||
}
|
||||
output.writeUnsigned(fieldCount);
|
||||
for (String field : annotation.getAvailableFields()) {
|
||||
output.writeUnsigned(symbolTable.lookup(field));
|
||||
writeAnnotationValue(output, annotation.getValue(field));
|
||||
}
|
||||
}
|
||||
|
||||
private CachedAnnotation readAnnotation(VarDataInput input) throws IOException {
|
||||
CachedAnnotation annotation = new CachedAnnotation();
|
||||
annotation.type = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
|
||||
int valueCount = input.readUnsigned();
|
||||
Map<String, AnnotationValue> fields = new HashMap<>();
|
||||
for (int i = 0; i < valueCount; ++i) {
|
||||
String name = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
|
||||
AnnotationValue value = readAnnotationValue(input);
|
||||
fields.put(name, value);
|
||||
}
|
||||
annotation.fields = fields;
|
||||
return annotation;
|
||||
}
|
||||
|
||||
public void writeAnnotationValue(VarDataOutput output, AnnotationValue value) throws IOException {
|
||||
output.writeUnsigned(value.getType());
|
||||
switch (value.getType()) {
|
||||
case AnnotationValue.ANNOTATION:
|
||||
writeAnnotation(output, value.getAnnotation());
|
||||
break;
|
||||
case AnnotationValue.BOOLEAN:
|
||||
output.writeUnsigned(value.getBoolean() ? 1 : 0);
|
||||
break;
|
||||
case AnnotationValue.BYTE:
|
||||
output.writeSigned(value.getByte());
|
||||
break;
|
||||
case AnnotationValue.CLASS:
|
||||
output.writeUnsigned(symbolTable.lookup(value.getJavaClass().toString()));
|
||||
break;
|
||||
case AnnotationValue.DOUBLE:
|
||||
output.writeDouble(value.getDouble());
|
||||
break;
|
||||
case AnnotationValue.ENUM:
|
||||
output.writeUnsigned(symbolTable.lookup(value.getEnumValue().getClassName()));
|
||||
output.writeUnsigned(symbolTable.lookup(value.getEnumValue().getFieldName()));
|
||||
break;
|
||||
case AnnotationValue.FLOAT:
|
||||
output.writeFloat(value.getFloat());
|
||||
break;
|
||||
case AnnotationValue.INT:
|
||||
output.writeSigned(value.getInt());
|
||||
break;
|
||||
case AnnotationValue.LIST: {
|
||||
List<AnnotationValue> list = value.getList();
|
||||
output.writeUnsigned(list.size());
|
||||
for (AnnotationValue item : list) {
|
||||
writeAnnotationValue(output, item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AnnotationValue.LONG:
|
||||
output.writeSigned(value.getLong());
|
||||
break;
|
||||
case AnnotationValue.SHORT:
|
||||
output.writeSigned(value.getShort());
|
||||
break;
|
||||
case AnnotationValue.STRING:
|
||||
output.write(value.getString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AnnotationValue readAnnotationValue(VarDataInput input) throws IOException {
|
||||
int type = input.readUnsigned();
|
||||
switch (type) {
|
||||
case AnnotationValue.ANNOTATION:
|
||||
return new AnnotationValue(readAnnotation(input));
|
||||
case AnnotationValue.BOOLEAN:
|
||||
return new AnnotationValue(input.readUnsigned() != 0);
|
||||
case AnnotationValue.BYTE:
|
||||
return new AnnotationValue((byte) input.readSigned());
|
||||
case AnnotationValue.CLASS:
|
||||
return new AnnotationValue(referenceCache.getCached(ValueType.parse(
|
||||
symbolTable.at(input.readUnsigned()))));
|
||||
case AnnotationValue.DOUBLE:
|
||||
return new AnnotationValue(input.readDouble());
|
||||
case AnnotationValue.ENUM: {
|
||||
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());
|
||||
case AnnotationValue.INT:
|
||||
return new AnnotationValue(input.readSigned());
|
||||
case AnnotationValue.LIST: {
|
||||
List<AnnotationValue> list = new ArrayList<>();
|
||||
int sz = input.readUnsigned();
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
list.add(readAnnotationValue(input));
|
||||
}
|
||||
return new AnnotationValue(list);
|
||||
}
|
||||
case AnnotationValue.LONG:
|
||||
return new AnnotationValue(input.readSignedLong());
|
||||
case AnnotationValue.SHORT:
|
||||
return new AnnotationValue((short) input.readSigned());
|
||||
case AnnotationValue.STRING:
|
||||
return new AnnotationValue(input.read());
|
||||
default:
|
||||
throw new RuntimeException("Unexpected annotation value type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
164
core/src/main/java/org/teavm/cache/ClassIO.java
vendored
164
core/src/main/java/org/teavm/cache/ClassIO.java
vendored
|
@ -20,19 +20,14 @@ 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.AnnotationContainerReader;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReader;
|
||||
|
@ -48,12 +43,14 @@ public class ClassIO {
|
|||
private ReferenceCache referenceCache;
|
||||
private SymbolTable symbolTable;
|
||||
private ProgramIO programIO;
|
||||
private AnnotationIO annotationIO;
|
||||
|
||||
public ClassIO(ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable,
|
||||
SymbolTable varTable) {
|
||||
this.referenceCache = referenceCache;
|
||||
this.symbolTable = symbolTable;
|
||||
programIO = new ProgramIO(referenceCache, symbolTable, fileTable, varTable);
|
||||
annotationIO = new AnnotationIO(referenceCache, symbolTable);
|
||||
}
|
||||
|
||||
public void writeClass(OutputStream stream, ClassReader cls) throws IOException {
|
||||
|
@ -66,7 +63,7 @@ public class ClassIO {
|
|||
for (String iface : cls.getInterfaces()) {
|
||||
output.writeUnsigned(symbolTable.lookup(iface));
|
||||
}
|
||||
writeAnnotations(output, cls.getAnnotations());
|
||||
annotationIO.writeAnnotations(output, cls.getAnnotations());
|
||||
output.writeUnsigned(cls.getFields().size());
|
||||
for (FieldReader field : cls.getFields()) {
|
||||
writeField(output, field);
|
||||
|
@ -93,7 +90,7 @@ public class ClassIO {
|
|||
interfaces.add(referenceCache.getCached(symbolTable.at(input.readUnsigned())));
|
||||
}
|
||||
cls.interfaces = Collections.unmodifiableSet(interfaces);
|
||||
cls.annotations = readAnnotations(input);
|
||||
cls.annotations = annotationIO.readAnnotations(input);
|
||||
|
||||
Map<String, CachedField> fields = new LinkedHashMap<>();
|
||||
int fieldCount = input.readUnsigned();
|
||||
|
@ -120,7 +117,7 @@ public class ClassIO {
|
|||
output.writeUnsigned(field.getLevel().ordinal());
|
||||
output.writeUnsigned(packModifiers(field.readModifiers()));
|
||||
writeFieldValue(output, field.getInitialValue());
|
||||
writeAnnotations(output, field.getAnnotations());
|
||||
annotationIO.writeAnnotations(output, field.getAnnotations());
|
||||
}
|
||||
|
||||
private CachedField readField(String className, VarDataInput input) throws IOException {
|
||||
|
@ -130,7 +127,7 @@ public class ClassIO {
|
|||
field.level = accessLevels[input.readUnsigned()];
|
||||
field.modifiers = unpackModifiers(input.readUnsigned());
|
||||
field.initialValue = readFieldValue(input);
|
||||
field.annotations = readAnnotations(input);
|
||||
field.annotations = annotationIO.readAnnotations(input);
|
||||
field.ownerName = className;
|
||||
field.reference = referenceCache.getCached(new FieldReference(className, field.name));
|
||||
return field;
|
||||
|
@ -181,15 +178,15 @@ public class ClassIO {
|
|||
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
|
||||
output.writeUnsigned(method.getLevel().ordinal());
|
||||
output.writeUnsigned(packModifiers(method.readModifiers()));
|
||||
writeAnnotations(output, method.getAnnotations());
|
||||
annotationIO.writeAnnotations(output, method.getAnnotations());
|
||||
|
||||
for (AnnotationContainerReader parameterAnnotation : method.getParameterAnnotations()) {
|
||||
writeAnnotations(output, parameterAnnotation);
|
||||
annotationIO.writeAnnotations(output, parameterAnnotation);
|
||||
}
|
||||
|
||||
if (method.getAnnotationDefault() != null) {
|
||||
output.writeUnsigned(1);
|
||||
writeAnnotationValue(output, method.getAnnotationDefault());
|
||||
annotationIO.writeAnnotationValue(output, method.getAnnotationDefault());
|
||||
} else {
|
||||
output.writeUnsigned(0);
|
||||
}
|
||||
|
@ -212,17 +209,17 @@ public class ClassIO {
|
|||
method.reference = referenceCache.getCached(className, descriptor);
|
||||
method.level = accessLevels[input.readUnsigned()];
|
||||
method.modifiers = unpackModifiers(input.readUnsigned());
|
||||
method.annotations = readAnnotations(input);
|
||||
method.annotations = annotationIO.readAnnotations(input);
|
||||
method.ownerName = className;
|
||||
method.name = descriptor.getName();
|
||||
|
||||
method.parameterAnnotations = new CachedAnnotations[descriptor.parameterCount()];
|
||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||
method.parameterAnnotations[i] = readAnnotations(input);
|
||||
method.parameterAnnotations[i] = annotationIO.readAnnotations(input);
|
||||
}
|
||||
|
||||
if (input.readUnsigned() != 0) {
|
||||
method.annotationDefault = readAnnotationValue(input);
|
||||
method.annotationDefault = annotationIO.readAnnotationValue(input);
|
||||
}
|
||||
|
||||
if (input.readUnsigned() != 0) {
|
||||
|
@ -239,143 +236,6 @@ public class ClassIO {
|
|||
return method;
|
||||
}
|
||||
|
||||
private void writeAnnotations(VarDataOutput output, AnnotationContainerReader annotations) throws IOException {
|
||||
List<AnnotationReader> annotationList = new ArrayList<>();
|
||||
for (AnnotationReader annot : annotations.all()) {
|
||||
annotationList.add(annot);
|
||||
}
|
||||
output.writeUnsigned(annotationList.size());
|
||||
for (AnnotationReader annot : annotationList) {
|
||||
writeAnnotation(output, annot);
|
||||
}
|
||||
}
|
||||
|
||||
private CachedAnnotations readAnnotations(VarDataInput input) throws IOException {
|
||||
Map<String, CachedAnnotation> annotations = new HashMap<>();
|
||||
int annotCount = input.readUnsigned();
|
||||
for (int i = 0; i < annotCount; ++i) {
|
||||
CachedAnnotation annot = readAnnotation(input);
|
||||
annotations.put(annot.type, annot);
|
||||
}
|
||||
return new CachedAnnotations(annotations);
|
||||
}
|
||||
|
||||
private void writeAnnotation(VarDataOutput output, AnnotationReader annotation) throws IOException {
|
||||
output.writeUnsigned(symbolTable.lookup(annotation.getType()));
|
||||
int fieldCount = 0;
|
||||
for (@SuppressWarnings("unused") String field : annotation.getAvailableFields()) {
|
||||
++fieldCount;
|
||||
}
|
||||
output.writeUnsigned(fieldCount);
|
||||
for (String field : annotation.getAvailableFields()) {
|
||||
output.writeUnsigned(symbolTable.lookup(field));
|
||||
writeAnnotationValue(output, annotation.getValue(field));
|
||||
}
|
||||
}
|
||||
|
||||
private CachedAnnotation readAnnotation(VarDataInput input) throws IOException {
|
||||
CachedAnnotation annotation = new CachedAnnotation();
|
||||
annotation.type = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
|
||||
int valueCount = input.readUnsigned();
|
||||
Map<String, AnnotationValue> fields = new HashMap<>();
|
||||
for (int i = 0; i < valueCount; ++i) {
|
||||
String name = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
|
||||
AnnotationValue value = readAnnotationValue(input);
|
||||
fields.put(name, value);
|
||||
}
|
||||
annotation.fields = fields;
|
||||
return annotation;
|
||||
}
|
||||
|
||||
private void writeAnnotationValue(VarDataOutput output, AnnotationValue value) throws IOException {
|
||||
output.writeUnsigned(value.getType());
|
||||
switch (value.getType()) {
|
||||
case AnnotationValue.ANNOTATION:
|
||||
writeAnnotation(output, value.getAnnotation());
|
||||
break;
|
||||
case AnnotationValue.BOOLEAN:
|
||||
output.writeUnsigned(value.getBoolean() ? 1 : 0);
|
||||
break;
|
||||
case AnnotationValue.BYTE:
|
||||
output.writeSigned(value.getByte());
|
||||
break;
|
||||
case AnnotationValue.CLASS:
|
||||
output.writeUnsigned(symbolTable.lookup(value.getJavaClass().toString()));
|
||||
break;
|
||||
case AnnotationValue.DOUBLE:
|
||||
output.writeDouble(value.getDouble());
|
||||
break;
|
||||
case AnnotationValue.ENUM:
|
||||
output.writeUnsigned(symbolTable.lookup(value.getEnumValue().getClassName()));
|
||||
output.writeUnsigned(symbolTable.lookup(value.getEnumValue().getFieldName()));
|
||||
break;
|
||||
case AnnotationValue.FLOAT:
|
||||
output.writeFloat(value.getFloat());
|
||||
break;
|
||||
case AnnotationValue.INT:
|
||||
output.writeSigned(value.getInt());
|
||||
break;
|
||||
case AnnotationValue.LIST: {
|
||||
List<AnnotationValue> list = value.getList();
|
||||
output.writeUnsigned(list.size());
|
||||
for (AnnotationValue item : list) {
|
||||
writeAnnotationValue(output, item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AnnotationValue.LONG:
|
||||
output.writeSigned(value.getLong());
|
||||
break;
|
||||
case AnnotationValue.SHORT:
|
||||
output.writeSigned(value.getShort());
|
||||
break;
|
||||
case AnnotationValue.STRING:
|
||||
output.write(value.getString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationValue readAnnotationValue(VarDataInput input) throws IOException {
|
||||
int type = input.readUnsigned();
|
||||
switch (type) {
|
||||
case AnnotationValue.ANNOTATION:
|
||||
return new AnnotationValue(readAnnotation(input));
|
||||
case AnnotationValue.BOOLEAN:
|
||||
return new AnnotationValue(input.readUnsigned() != 0);
|
||||
case AnnotationValue.BYTE:
|
||||
return new AnnotationValue((byte) input.readSigned());
|
||||
case AnnotationValue.CLASS:
|
||||
return new AnnotationValue(referenceCache.getCached(ValueType.parse(
|
||||
symbolTable.at(input.readUnsigned()))));
|
||||
case AnnotationValue.DOUBLE:
|
||||
return new AnnotationValue(input.readDouble());
|
||||
case AnnotationValue.ENUM: {
|
||||
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());
|
||||
case AnnotationValue.INT:
|
||||
return new AnnotationValue(input.readSigned());
|
||||
case AnnotationValue.LIST: {
|
||||
List<AnnotationValue> list = new ArrayList<>();
|
||||
int sz = input.readUnsigned();
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
list.add(readAnnotationValue(input));
|
||||
}
|
||||
return new AnnotationValue(list);
|
||||
}
|
||||
case AnnotationValue.LONG:
|
||||
return new AnnotationValue(input.readSignedLong());
|
||||
case AnnotationValue.SHORT:
|
||||
return new AnnotationValue((short) input.readSigned());
|
||||
case AnnotationValue.STRING:
|
||||
return new AnnotationValue(input.read());
|
||||
default:
|
||||
throw new RuntimeException("Unexpected annotation value type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private int packModifiers(Set<ElementModifier> modifiers) {
|
||||
int result = 0;
|
||||
|
|
|
@ -80,7 +80,7 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt
|
|||
|
||||
private Entry getEntry(String name) {
|
||||
return cache.computeIfAbsent(name, className -> {
|
||||
ClassReader cls = provider.apply(className);
|
||||
ClassReader cls = provider != null ? provider.apply(className) : null;
|
||||
Entry en = new Entry();
|
||||
if (cls != null) {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
|
|
|
@ -88,12 +88,14 @@ import org.teavm.model.instructions.SwitchInstruction;
|
|||
import org.teavm.model.instructions.SwitchTableEntry;
|
||||
import org.teavm.model.instructions.SwitchTableEntryReader;
|
||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||
import org.teavm.model.util.ModelUtils;
|
||||
|
||||
public class ProgramIO {
|
||||
private SymbolTable symbolTable;
|
||||
private SymbolTable fileTable;
|
||||
private SymbolTable variableTable;
|
||||
private ReferenceCache referenceCache;
|
||||
private AnnotationIO annotationIO;
|
||||
private static BinaryOperation[] binaryOperations = BinaryOperation.values();
|
||||
private static NumericOperandType[] numericOperandTypes = NumericOperandType.values();
|
||||
private static IntegerSubtype[] integerSubtypes = IntegerSubtype.values();
|
||||
|
@ -108,6 +110,7 @@ public class ProgramIO {
|
|||
this.symbolTable = symbolTable;
|
||||
this.fileTable = fileTable;
|
||||
this.variableTable = variableTable;
|
||||
annotationIO = new AnnotationIO(referenceCache, symbolTable);
|
||||
}
|
||||
|
||||
public void write(ProgramReader program, OutputStream output) throws IOException {
|
||||
|
@ -149,6 +152,7 @@ public class ProgramIO {
|
|||
}
|
||||
data.writeUnsigned(0);
|
||||
}
|
||||
annotationIO.writeAnnotations(data, program.getAnnotations());
|
||||
}
|
||||
|
||||
public Program read(InputStream input) throws IOException {
|
||||
|
@ -230,6 +234,7 @@ public class ProgramIO {
|
|||
}
|
||||
}
|
||||
}
|
||||
ModelUtils.copyAnnotations(annotationIO.readAnnotations(data), program.getAnnotations());
|
||||
return program;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
package org.teavm.common;
|
||||
|
||||
import com.carrotsearch.hppc.IntArrayList;
|
||||
import com.carrotsearch.hppc.IntHashSet;
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -157,7 +157,8 @@ public class GraphIndexer {
|
|||
List<WeightedNode> succList = new ArrayList<>(successors.length);
|
||||
IntegerArray orderedSuccessors = new IntegerArray(successors.length);
|
||||
if (terminalNodes.size() > 0) {
|
||||
IntSet loopNodes = IntHashSet.from(findNaturalLoop(node, terminalNodes.getAll()));
|
||||
int[] loopNodeList = findNaturalLoop(node, terminalNodes.getAll());
|
||||
IntSet loopNodes = IntHashSet.from(loopNodeList);
|
||||
for (int succ : successors) {
|
||||
if (loopNodes.contains(succ)) {
|
||||
succList.add(new WeightedNode(succ, priorities[succ], weights[succ]));
|
||||
|
@ -170,8 +171,8 @@ public class GraphIndexer {
|
|||
|
||||
IntSet outerSuccessors = new IntHashSet(successors.length);
|
||||
succList.clear();
|
||||
for (IntCursor loopNode : loopNodes) {
|
||||
for (int succ : graph.outgoingEdges(loopNode.value)) {
|
||||
for (int loopNode : loopNodeList) {
|
||||
for (int succ : graph.outgoingEdges(loopNode)) {
|
||||
if (!loopNodes.contains(succ)) {
|
||||
if (outerSuccessors.add(succ)) {
|
||||
succList.add(new WeightedNode(succ, priorities[succ], weights[succ]));
|
||||
|
@ -206,7 +207,9 @@ public class GraphIndexer {
|
|||
|
||||
private int[] findNaturalLoop(int head, int[] terminals) {
|
||||
IntSet loop = new IntHashSet();
|
||||
IntArrayList loopList = new IntArrayList();
|
||||
loop.add(head);
|
||||
loopList.add(head);
|
||||
IntegerStack stack = new IntegerStack(1);
|
||||
for (int pred : terminals) {
|
||||
stack.push(pred);
|
||||
|
@ -216,11 +219,12 @@ public class GraphIndexer {
|
|||
if (!loop.add(node)) {
|
||||
continue;
|
||||
}
|
||||
loopList.add(node);
|
||||
for (int pred : graph.incomingEdges(node)) {
|
||||
stack.push(pred);
|
||||
}
|
||||
}
|
||||
return loop.toArray();
|
||||
return loopList.toArray();
|
||||
}
|
||||
|
||||
public int nodeAt(int index) {
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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 java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.VariableReader;
|
||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
|
||||
class ClassClosureAnalyzer extends AbstractInstructionReader {
|
||||
private ClassReaderSource classSource;
|
||||
private Set<String> result = new LinkedHashSet<>();
|
||||
|
||||
ClassClosureAnalyzer(ClassReaderSource classSource) {
|
||||
this.classSource = classSource;
|
||||
}
|
||||
|
||||
static Set<? extends String> build(ClassReaderSource classSource,
|
||||
Collection<? extends String> initialClasses) {
|
||||
ClassClosureAnalyzer analyzer = new ClassClosureAnalyzer(classSource);
|
||||
for (String className : initialClasses) {
|
||||
analyzer.build(className);
|
||||
}
|
||||
return analyzer.result;
|
||||
}
|
||||
|
||||
void build(String className) {
|
||||
if (!result.add(className)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls.getParent() != null) {
|
||||
build(cls.getParent());
|
||||
}
|
||||
for (String itf : cls.getInterfaces()) {
|
||||
build(itf);
|
||||
}
|
||||
|
||||
for (FieldReader field : cls.getFields()) {
|
||||
build(field.getType());
|
||||
}
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
build(method.getResultType());
|
||||
for (ValueType paramType : method.getParameterTypes()) {
|
||||
build(paramType);
|
||||
}
|
||||
if (method.getProgram() != null) {
|
||||
for (BasicBlockReader block : method.getProgram().getBasicBlocks()) {
|
||||
block.readAllInstructions(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build(ValueType type) {
|
||||
while (type instanceof ValueType.Array) {
|
||||
type = ((ValueType.Array) type).getItemType();
|
||||
}
|
||||
if (type instanceof ValueType.Object) {
|
||||
build(((ValueType.Object) type).getClassName());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void classConstant(VariableReader receiver, ValueType cst) {
|
||||
build(cst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
|
||||
build(targetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
|
||||
build(itemType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createArray(VariableReader receiver, ValueType itemType,
|
||||
List<? extends VariableReader> dimensions) {
|
||||
build(itemType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
|
||||
ValueType fieldType) {
|
||||
build(fieldType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putField(VariableReader instance, FieldReference field, VariableReader value, ValueType fieldType) {
|
||||
build(fieldType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments, InvocationType type) {
|
||||
build(method.getReturnType());
|
||||
for (ValueType paramType : method.getParameterTypes()) {
|
||||
build(paramType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
|
||||
build(type);
|
||||
}
|
||||
}
|
|
@ -776,7 +776,8 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
|
|||
unprocessedClassSource = null;
|
||||
classSource.innerHierarchy = null;
|
||||
|
||||
agentClassSource = classSourcePacker.pack(classSource, classSource.cache.keySet());
|
||||
agentClassSource = classSourcePacker.pack(classSource,
|
||||
ClassClosureAnalyzer.build(classSource, new ArrayList<>(classSource.cache.keySet())));
|
||||
if (classSource != agentClassSource) {
|
||||
classHierarchy = new ClassHierarchy(agentClassSource);
|
||||
generatedClassNames.addAll(classSource.getGeneratedClassNames());
|
||||
|
|
|
@ -23,6 +23,7 @@ public class Program implements ProgramReader {
|
|||
private List<Variable> variables = new ArrayList<>();
|
||||
private boolean packed;
|
||||
private int lastUsedRegister;
|
||||
private AnnotationContainer annotations = new AnnotationContainer();
|
||||
|
||||
public BasicBlock createBasicBlock() {
|
||||
BasicBlock block = new BasicBlock(this, basicBlocks.size());
|
||||
|
@ -149,4 +150,9 @@ public class Program implements ProgramReader {
|
|||
}
|
||||
return variables.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationContainer getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,4 +25,6 @@ public interface ProgramReader {
|
|||
int variableCount();
|
||||
|
||||
VariableReader variableAt(int index);
|
||||
|
||||
AnnotationContainerReader getAnnotations();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,19 @@
|
|||
package org.teavm.model.lowlevel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.model.AnnotationContainer;
|
||||
import org.teavm.model.AnnotationContainerReader;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.Program;
|
||||
|
||||
public class CallSiteDescriptor {
|
||||
private int id;
|
||||
|
@ -39,4 +51,86 @@ public class CallSiteDescriptor {
|
|||
public List<ExceptionHandlerDescriptor> getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static void save(Collection<? extends CallSiteDescriptor> descriptors, AnnotationContainer annotations) {
|
||||
List<AnnotationValue> descriptorsValue = new ArrayList<>();
|
||||
for (CallSiteDescriptor descriptor : descriptors) {
|
||||
AnnotationHolder descriptorAnnot = new AnnotationHolder(CallSiteDescriptorAnnot.class.getName());
|
||||
descriptorAnnot.getValues().put("id", new AnnotationValue(descriptor.id));
|
||||
descriptorAnnot.getValues().put("location", new AnnotationValue(descriptor.location.save()));
|
||||
List<AnnotationValue> handlersValue = descriptor.handlers.stream()
|
||||
.map(h -> new AnnotationValue(h.save()))
|
||||
.collect(Collectors.toList());
|
||||
descriptorAnnot.getValues().put("handlers", new AnnotationValue(handlersValue));
|
||||
descriptorsValue.add(new AnnotationValue(descriptorAnnot));
|
||||
}
|
||||
|
||||
AnnotationHolder descriptorsAnnot = new AnnotationHolder(CallSiteDescriptorsAnnot.class.getName());
|
||||
descriptorsAnnot.getValues().put("value", new AnnotationValue(descriptorsValue));
|
||||
annotations.add(descriptorsAnnot);
|
||||
}
|
||||
|
||||
public static Collection<? extends CallSiteDescriptor> load(AnnotationContainerReader annotations) {
|
||||
AnnotationReader descriptorsAnnot = annotations.get(CallSiteDescriptorsAnnot.class.getName());
|
||||
if (descriptorsAnnot == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<CallSiteDescriptor> descriptors = new ArrayList<>();
|
||||
for (AnnotationValue descriptorValue : descriptorsAnnot.getValue("value").getList()) {
|
||||
AnnotationReader descriptorAnnot = descriptorValue.getAnnotation();
|
||||
int id = descriptorAnnot.getValue("id").getInt();
|
||||
CallSiteLocation location = CallSiteLocation.load(descriptorAnnot.getValue("location").getAnnotation());
|
||||
List<ExceptionHandlerDescriptor> handlers = descriptorAnnot.getValue("handlers").getList().stream()
|
||||
.map(a -> ExceptionHandlerDescriptor.load(a.getAnnotation()))
|
||||
.collect(Collectors.toList());
|
||||
CallSiteDescriptor descriptor = new CallSiteDescriptor(id, location);
|
||||
descriptor.getHandlers().addAll(handlers);
|
||||
descriptors.add(descriptor);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public static List<? extends CallSiteDescriptor> extract(Program program) {
|
||||
List<CallSiteDescriptor> result = new ArrayList<>();
|
||||
extractTo(load(program.getAnnotations()), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<? extends CallSiteDescriptor> extract(ClassReaderSource classes,
|
||||
Collection<? extends String> classNames) {
|
||||
List<CallSiteDescriptor> result = new ArrayList<>();
|
||||
for (String className : classNames) {
|
||||
ClassReader cls = classes.get(className);
|
||||
if (cls == null) {
|
||||
continue;
|
||||
}
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
if (method.getProgram() != null) {
|
||||
extractTo(load(method.getProgram().getAnnotations()), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void extractTo(Collection<? extends CallSiteDescriptor> descriptors,
|
||||
List<CallSiteDescriptor> result) {
|
||||
for (CallSiteDescriptor descriptor : descriptors) {
|
||||
if (descriptor.id >= result.size()) {
|
||||
result.addAll(Collections.nCopies(descriptor.id - result.size() + 1, null));
|
||||
}
|
||||
result.set(descriptor.id, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
static AnnotationValue saveNullableString(String s) {
|
||||
return new AnnotationValue(s != null ? "1" + s : "0");
|
||||
}
|
||||
|
||||
static String loadNullableString(AnnotationValue value) {
|
||||
String s = value.getString();
|
||||
return s.startsWith("0") ? null : s.substring(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.model.lowlevel;
|
||||
|
||||
@interface CallSiteDescriptorAnnot {
|
||||
int id();
|
||||
|
||||
ExceptionHandlerDescriptorAnnot[] handlers();
|
||||
|
||||
CallSiteLocationAnnot location();
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.model.lowlevel;
|
||||
|
||||
@interface CallSiteDescriptorsAnnot {
|
||||
CallSiteDescriptorAnnot[] value();
|
||||
}
|
|
@ -16,6 +16,9 @@
|
|||
package org.teavm.model.lowlevel;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
|
||||
public class CallSiteLocation {
|
||||
private String fileName;
|
||||
|
@ -63,4 +66,21 @@ public class CallSiteLocation {
|
|||
public int hashCode() {
|
||||
return Objects.hash(fileName, className, methodName, lineNumber);
|
||||
}
|
||||
|
||||
public AnnotationReader save() {
|
||||
AnnotationHolder annotation = new AnnotationHolder(CallSiteLocationAnnot.class.getName());
|
||||
annotation.getValues().put("fileName", CallSiteDescriptor.saveNullableString(fileName));
|
||||
annotation.getValues().put("className", CallSiteDescriptor.saveNullableString(className));
|
||||
annotation.getValues().put("methodName", CallSiteDescriptor.saveNullableString(methodName));
|
||||
annotation.getValues().put("lineNumber", new AnnotationValue(lineNumber));
|
||||
return annotation;
|
||||
}
|
||||
|
||||
public static CallSiteLocation load(AnnotationReader reader) {
|
||||
return new CallSiteLocation(
|
||||
CallSiteDescriptor.loadNullableString(reader.getValue("fileName")),
|
||||
CallSiteDescriptor.loadNullableString(reader.getValue("className")),
|
||||
CallSiteDescriptor.loadNullableString(reader.getValue("methodName")),
|
||||
reader.getValue("lineNumber").getInt());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.model.lowlevel;
|
||||
|
||||
@interface CallSiteLocationAnnot {
|
||||
String fileName();
|
||||
|
||||
String className();
|
||||
|
||||
String methodName();
|
||||
|
||||
int lineNumber();
|
||||
}
|
|
@ -15,6 +15,10 @@
|
|||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
|
||||
public class ExceptionHandlerDescriptor {
|
||||
private int id;
|
||||
private String className;
|
||||
|
@ -31,4 +35,17 @@ public class ExceptionHandlerDescriptor {
|
|||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public AnnotationReader save() {
|
||||
AnnotationHolder annot = new AnnotationHolder(ExceptionHandlerDescriptorAnnot.class.getName());
|
||||
annot.getValues().put("id", new AnnotationValue(id));
|
||||
annot.getValues().put("className", CallSiteDescriptor.saveNullableString(className));
|
||||
return annot;
|
||||
}
|
||||
|
||||
public static ExceptionHandlerDescriptor load(AnnotationReader annot) {
|
||||
return new ExceptionHandlerDescriptor(
|
||||
annot.getValue("id").getInt(),
|
||||
CallSiteDescriptor.loadNullableString(annot.getValue("className")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.model.lowlevel;
|
||||
|
||||
@interface ExceptionHandlerDescriptorAnnot {
|
||||
int id();
|
||||
|
||||
String className();
|
||||
}
|
|
@ -33,27 +33,26 @@ import org.teavm.model.instructions.JumpInstruction;
|
|||
import org.teavm.runtime.ShadowStack;
|
||||
|
||||
public class ShadowStackTransformer {
|
||||
private Characteristics managedMethodRepository;
|
||||
private Characteristics characteristics;
|
||||
private GCShadowStackContributor gcContributor;
|
||||
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
|
||||
public ShadowStackTransformer(Characteristics managedMethodRepository) {
|
||||
gcContributor = new GCShadowStackContributor(managedMethodRepository);
|
||||
this.managedMethodRepository = managedMethodRepository;
|
||||
}
|
||||
|
||||
public List<CallSiteDescriptor> getCallSites() {
|
||||
return callSites;
|
||||
public ShadowStackTransformer(Characteristics characteristics) {
|
||||
gcContributor = new GCShadowStackContributor(characteristics);
|
||||
this.characteristics = characteristics;
|
||||
}
|
||||
|
||||
public void apply(Program program, MethodReader method) {
|
||||
if (!managedMethodRepository.isManaged(method.getReference())) {
|
||||
if (!characteristics.isManaged(method.getReference())) {
|
||||
return;
|
||||
}
|
||||
|
||||
int shadowStackSize = gcContributor.contribute(program, method);
|
||||
boolean exceptions = new ExceptionHandlingShadowStackContributor(managedMethodRepository, callSites,
|
||||
int callSiteStartIndex = callSites.size();
|
||||
boolean exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites,
|
||||
method.getReference(), program).contribute();
|
||||
List<CallSiteDescriptor> programCallSites = callSites.subList(callSiteStartIndex, callSites.size());
|
||||
CallSiteDescriptor.save(programCallSites, program.getAnnotations());
|
||||
|
||||
if (shadowStackSize > 0 || exceptions) {
|
||||
addStackAllocation(program, shadowStackSize);
|
||||
|
|
|
@ -91,6 +91,7 @@ public final class ProgramUtils {
|
|||
BasicBlock blockCopy = copy.basicBlockAt(i);
|
||||
copyBasicBlock(block, blockCopy);
|
||||
}
|
||||
ModelUtils.copyAnnotations(program.getAnnotations(), copy.getAnnotations());
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
|
|
@ -342,6 +342,7 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
|
|||
public void visit(GetFieldInstruction insn) {
|
||||
String className = classNameMapper.apply(insn.getField().getClassName());
|
||||
insn.setField(referenceCache.getCached(new FieldReference(className, insn.getField().getFieldName())));
|
||||
insn.setFieldType(rename(insn.getFieldType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -350,6 +351,7 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
|
|||
if (className != insn.getField().getClassName()) {
|
||||
insn.setField(referenceCache.getCached(new FieldReference(className, insn.getField().getFieldName())));
|
||||
}
|
||||
insn.setFieldType(rename(insn.getFieldType()));
|
||||
}
|
||||
@Override
|
||||
public void visit(InvokeInstruction insn) {
|
||||
|
|
|
@ -26,13 +26,13 @@ public final class ExceptionHandling {
|
|||
private ExceptionHandling() {
|
||||
}
|
||||
|
||||
public static native CallSite findCallSiteById(int id);
|
||||
public static native CallSite findCallSiteById(int id, Address frame);
|
||||
|
||||
public static void printStack() {
|
||||
Address stackFrame = ShadowStack.getStackTop();
|
||||
while (stackFrame != null) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
CallSite callSite = findCallSiteById(callSiteId);
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
CallSiteLocation location = callSite.location;
|
||||
|
||||
Console.printString(" at ");
|
||||
|
@ -74,7 +74,7 @@ public final class ExceptionHandling {
|
|||
Address stackFrame = ShadowStack.getStackTop();
|
||||
stackLoop: while (stackFrame != null) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
CallSite callSite = findCallSiteById(callSiteId);
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
ExceptionHandler handler = callSite.firstHandler;
|
||||
|
||||
for (int i = 0; i < callSite.handlerCount; ++i) {
|
||||
|
@ -118,7 +118,7 @@ public final class ExceptionHandling {
|
|||
int index = 0;
|
||||
while (stackFrame != null && index < target.length) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
CallSite callSite = findCallSiteById(callSiteId);
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
CallSiteLocation location = callSite.location;
|
||||
StackTraceElement element = createElement(location != null ? location.className : "",
|
||||
location != null ? location.methodName : "", location != null ? location.fileName : null,
|
||||
|
|
|
@ -45,6 +45,8 @@ public class RuntimeClass extends RuntimeObject {
|
|||
public IsSupertypeFunction isSupertypeOf;
|
||||
public InitFunction init;
|
||||
public RuntimeClass parent;
|
||||
public int superinterfaceCount;
|
||||
public RuntimeClassPointer superinterfaces;
|
||||
public Address enumValues;
|
||||
public Address layout;
|
||||
public RuntimeObject simpleName;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.runtime;
|
||||
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
|
||||
@Unmanaged
|
||||
public class RuntimeClassPointer extends Structure {
|
||||
public RuntimeClass value;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.vm;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class IncrementalDirectoryBuildTarget implements BuildTarget {
|
||||
private File directory;
|
||||
private Set<String> writtenFiles = new HashSet<>();
|
||||
private Set<String> formerWrittenFiles = new HashSet<>();
|
||||
|
||||
public IncrementalDirectoryBuildTarget(File directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
for (String fileName : formerWrittenFiles) {
|
||||
if (!writtenFiles.contains(fileName)) {
|
||||
new File(directory, fileName).delete();
|
||||
}
|
||||
}
|
||||
formerWrittenFiles.clear();
|
||||
formerWrittenFiles.addAll(writtenFiles);
|
||||
writtenFiles.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream createResource(String fileName) {
|
||||
writtenFiles.add(fileName);
|
||||
return new OutputStreamImpl(new File(directory, fileName));
|
||||
}
|
||||
|
||||
static class OutputStreamImpl extends OutputStream {
|
||||
private File file;
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
|
||||
OutputStreamImpl(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
checkNotClosed();
|
||||
bytes.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
checkNotClosed();
|
||||
bytes.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
checkNotClosed();
|
||||
byte[] data = bytes.toByteArray();
|
||||
bytes = null;
|
||||
if (isChanged(file, data)) {
|
||||
file.getParentFile().mkdirs();
|
||||
try (OutputStream output = new BufferedOutputStream(new FileOutputStream(file))) {
|
||||
output.write(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isChanged(File file, byte[] data) throws IOException {
|
||||
if (!file.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
InputStream input = new BufferedInputStream(new FileInputStream(file));
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int index = 0;
|
||||
while (true) {
|
||||
int bytesRead = input.read(buffer);
|
||||
if (bytesRead < 0) {
|
||||
break;
|
||||
}
|
||||
if (bytesRead + index > data.length) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < bytesRead; ++i) {
|
||||
if (buffer[i] != data[index++]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index < data.length;
|
||||
}
|
||||
|
||||
private void checkNotClosed() throws IOException {
|
||||
if (bytes == null) {
|
||||
throw new IOException("Already closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
package org.teavm.vm;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -41,7 +40,7 @@ public class MemoryBuildTarget implements BuildTarget {
|
|||
}
|
||||
|
||||
@Override
|
||||
public OutputStream createResource(String fileName) throws IOException {
|
||||
public OutputStream createResource(String fileName) {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
data.put(fileName, stream);
|
||||
return stream;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <uchar.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -36,6 +35,8 @@ typedef struct TeaVM_Class {
|
|||
int32_t (*isSupertypeOf)(struct TeaVM_Class*);
|
||||
void (*init)();
|
||||
struct TeaVM_Class* superclass;
|
||||
int32_t superinterfaceCount;
|
||||
struct TeaVM_Class** superinterfaces;
|
||||
void* enumValues;
|
||||
void* layout;
|
||||
TeaVM_Object* simpleName;
|
||||
|
@ -97,15 +98,35 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
|
|||
return obj == NULL || cls(TEAVM_CLASS_OF(obj)) ? obj : teavm_throwClassCastException();
|
||||
}
|
||||
|
||||
#define TEAVM_ALLOC_STACK(size) \
|
||||
void* teavm__shadowStack__[(size) + 3]; \
|
||||
teavm__shadowStack__[0] = teavm_stackTop; \
|
||||
teavm__shadowStack__[2] = (void*) size; \
|
||||
teavm_stackTop = teavm__shadowStack__
|
||||
#ifdef TEAVM_INCREMENTAL
|
||||
|
||||
#define TEAVM_ALLOC_STACK_DEF(size, callSites) \
|
||||
void* teavm__shadowStack__[(size) + 4]; \
|
||||
teavm__shadowStack__[0] = teavm_stackTop; \
|
||||
teavm__shadowStack__[2] = (void*) size; \
|
||||
teavm__shadowStack__[3] = (void*) (callSites); \
|
||||
teavm_stackTop = teavm__shadowStack__
|
||||
|
||||
#define TEAVM_STACK_HEADER_ADD_SIZE 1
|
||||
|
||||
#else
|
||||
|
||||
#define TEAVM_ALLOC_STACK(size) \
|
||||
void* teavm__shadowStack__[(size) + 3]; \
|
||||
teavm__shadowStack__[0] = teavm_stackTop; \
|
||||
teavm__shadowStack__[2] = (void*) size; \
|
||||
teavm_stackTop = teavm__shadowStack__
|
||||
|
||||
#define TEAVM_STACK_HEADER_ADD_SIZE 0
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define TEAVM_RELEASE_STACK teavm_stackTop = teavm__shadowStack__[0]
|
||||
#define TEAVM_GC_ROOT(index, ptr) teavm__shadowStack__[3 + (index)] = ptr
|
||||
#define TEAVM_GC_ROOT_RELEASE(index) teavm__shadowStack__[3 + (index)] = NULL
|
||||
#define TEAVM_GC_ROOT(index, ptr) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = ptr
|
||||
#define TEAVM_GC_ROOT_RELEASE(index) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = NULL
|
||||
#define TEAVM_GC_ROOTS_COUNT(ptr) ((int32_t) (intptr_t) ((void**) (ptr))[2])
|
||||
#define TEAVM_GET_GC_ROOTS(ptr) (((void**) (ptr)) + 3 + TEAVM_STACK_HEADER_ADD_SIZE)
|
||||
#define TEAVM_CALL_SITE(id) (teavm__shadowStack__[1] = (void*) (id))
|
||||
#define TEAVM_EXCEPTION_HANDLER ((int32_t) (intptr_t) (teavm__shadowStack__[1]))
|
||||
#define TEAVM_SET_EXCEPTION_HANDLER(frame, id) (((void**) (frame))[1] = (void*) (intptr_t) (id))
|
||||
|
@ -113,6 +134,11 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
|
|||
#define TEAVM_ADDRESS_ADD(address, offset) ((char *) (address) + (offset))
|
||||
#define TEAVM_STRUCTURE_ADD(structure, address, offset) (((structure*) (address)) + offset)
|
||||
|
||||
#define TEAVM_NULL_STRING { \
|
||||
.characters = NULL, \
|
||||
.hashCode = 0 \
|
||||
}
|
||||
|
||||
#define TEAVM_STRING(length, hash, s) { \
|
||||
.characters = (TeaVM_Array*) & (struct { TeaVM_Array hdr; char16_t data[(length) + 1]; }) { \
|
||||
.hdr = { .size = length }, \
|
||||
|
|
1
pom.xml
1
pom.xml
|
@ -89,6 +89,7 @@
|
|||
<module>jso/impl</module>
|
||||
<module>html4j</module>
|
||||
<module>platform</module>
|
||||
<module>tools/c-incremental</module>
|
||||
<module>tools/core</module>
|
||||
<module>tools/maven</module>
|
||||
<module>tools/chrome-rdp</module>
|
||||
|
|
92
tools/c-incremental/pom.xml
Normal file
92
tools/c-incremental/pom.xml
Normal file
|
@ -0,0 +1,92 @@
|
|||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm</artifactId>
|
||||
<version>0.6.0-SNAPSHOT</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
<artifactId>teavm-c-incremental</artifactId>
|
||||
|
||||
<name>TeaVM Incremental C builder</name>
|
||||
<description>Incremental generator of C code</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-util</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.carrotsearch</groupId>
|
||||
<artifactId>hppc</artifactId>
|
||||
<version>0.7.3</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mozilla</groupId>
|
||||
<artifactId>rhino</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-tooling</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<configuration>
|
||||
<configLocation>../../checkstyle.xml</configLocation>
|
||||
<propertyExpansion>config_loc=${basedir}/../..</propertyExpansion>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.tooling.c.incremental;
|
||||
|
||||
import org.teavm.tooling.builder.BuildResult;
|
||||
|
||||
public interface BuilderListener {
|
||||
void compilationStarted();
|
||||
|
||||
void compilationProgress(double progress);
|
||||
|
||||
void compilationComplete(BuildResult result);
|
||||
|
||||
void compilationCancelled();
|
||||
}
|
|
@ -0,0 +1,510 @@
|
|||
/*
|
||||
* 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.tooling.c.incremental;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import org.teavm.backend.c.CTarget;
|
||||
import org.teavm.backend.c.generate.SimpleStringPool;
|
||||
import org.teavm.cache.InMemoryMethodNodeCache;
|
||||
import org.teavm.cache.InMemoryProgramCache;
|
||||
import org.teavm.cache.InMemorySymbolTable;
|
||||
import org.teavm.cache.MemoryCachedClassReaderSource;
|
||||
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;
|
||||
import org.teavm.parsing.resource.ClasspathResourceReader;
|
||||
import org.teavm.parsing.resource.ResourceClassHolderMapper;
|
||||
import org.teavm.tooling.EmptyTeaVMToolLog;
|
||||
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||
import org.teavm.tooling.TeaVMToolLog;
|
||||
import org.teavm.tooling.builder.SimpleBuildResult;
|
||||
import org.teavm.tooling.util.FileSystemWatcher;
|
||||
import org.teavm.vm.IncrementalDirectoryBuildTarget;
|
||||
import org.teavm.vm.TeaVM;
|
||||
import org.teavm.vm.TeaVMBuilder;
|
||||
import org.teavm.vm.TeaVMOptimizationLevel;
|
||||
import org.teavm.vm.TeaVMPhase;
|
||||
import org.teavm.vm.TeaVMProgressFeedback;
|
||||
import org.teavm.vm.TeaVMProgressListener;
|
||||
|
||||
public class IncrementalCBuilder {
|
||||
private String mainClass;
|
||||
private String[] classPath;
|
||||
private int minHeapSize = 32;
|
||||
private String targetPath;
|
||||
|
||||
private IncrementalDirectoryBuildTarget buildTarget;
|
||||
private FileSystemWatcher watcher;
|
||||
private TeaVMToolLog log = new EmptyTeaVMToolLog();
|
||||
private MemoryCachedClassReaderSource classSource;
|
||||
|
||||
private ReferenceCache referenceCache = new ReferenceCache();
|
||||
private SimpleStringPool stringPool = new SimpleStringPool();
|
||||
private InMemorySymbolTable symbolTable = new InMemorySymbolTable();
|
||||
private InMemorySymbolTable fileSymbolTable = new InMemorySymbolTable();
|
||||
private InMemorySymbolTable variableSymbolTable = new InMemorySymbolTable();
|
||||
private InMemoryProgramCache programCache;
|
||||
private InMemoryMethodNodeCache astCache;
|
||||
|
||||
private List<BuilderListener> listeners = new ArrayList<>();
|
||||
private final Set<ProgressHandler> progressHandlers = new LinkedHashSet<>();
|
||||
|
||||
private int lastReachedClasses;
|
||||
private final Object statusLock = new Object();
|
||||
private volatile boolean cancelRequested;
|
||||
private volatile boolean stopped;
|
||||
private boolean compiling;
|
||||
private double progress;
|
||||
private boolean waiting;
|
||||
private Thread buildThread;
|
||||
|
||||
public void setMainClass(String mainClass) {
|
||||
this.mainClass = mainClass;
|
||||
}
|
||||
|
||||
public void setClassPath(String[] classPath) {
|
||||
this.classPath = classPath;
|
||||
}
|
||||
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
public void setTargetPath(String targetPath) {
|
||||
this.targetPath = targetPath;
|
||||
}
|
||||
|
||||
public void setLog(TeaVMToolLog log) {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
public void addProgressHandler(ProgressHandler handler) {
|
||||
synchronized (progressHandlers) {
|
||||
progressHandlers.add(handler);
|
||||
}
|
||||
|
||||
double progress;
|
||||
synchronized (statusLock) {
|
||||
if (!compiling) {
|
||||
return;
|
||||
}
|
||||
progress = this.progress;
|
||||
}
|
||||
|
||||
handler.progress(progress);
|
||||
}
|
||||
|
||||
public void removeProgressHandler(ProgressHandler handler) {
|
||||
synchronized (progressHandlers) {
|
||||
progressHandlers.remove(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(BuilderListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void invalidateCache() {
|
||||
synchronized (statusLock) {
|
||||
if (compiling) {
|
||||
return;
|
||||
}
|
||||
astCache.invalidate();
|
||||
programCache.invalidate();
|
||||
classSource.invalidate();
|
||||
symbolTable.invalidate();
|
||||
fileSymbolTable.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void buildProject() {
|
||||
synchronized (statusLock) {
|
||||
if (waiting) {
|
||||
buildThread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelBuild() {
|
||||
synchronized (statusLock) {
|
||||
if (compiling) {
|
||||
cancelRequested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
buildThread = new Thread(this::run);
|
||||
buildThread.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stopped = true;
|
||||
synchronized (statusLock) {
|
||||
if (waiting) {
|
||||
buildThread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void run() {
|
||||
try {
|
||||
initBuilder();
|
||||
|
||||
while (!stopped) {
|
||||
buildOnce();
|
||||
|
||||
if (stopped) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
synchronized (statusLock) {
|
||||
waiting = true;
|
||||
}
|
||||
watcher.waitForChange(750);
|
||||
synchronized (statusLock) {
|
||||
waiting = false;
|
||||
}
|
||||
log.info("Changes detected. Recompiling.");
|
||||
} catch (InterruptedException e) {
|
||||
if (stopped) {
|
||||
break;
|
||||
}
|
||||
log.info("Build triggered by user");
|
||||
}
|
||||
|
||||
List<String> staleClasses = getChangedClasses(watcher.grabChangedFiles());
|
||||
if (staleClasses.size() > 15) {
|
||||
List<String> displayedStaleClasses = staleClasses.subList(0, 10);
|
||||
log.debug("Following classes changed (" + staleClasses.size() + "): "
|
||||
+ String.join(", ", displayedStaleClasses) + " and more...");
|
||||
} else {
|
||||
log.debug("Following classes changed (" + staleClasses.size() + "): "
|
||||
+ String.join(", ", staleClasses));
|
||||
}
|
||||
|
||||
classSource.evict(staleClasses);
|
||||
}
|
||||
log.info("Build process stopped");
|
||||
} catch (Throwable e) {
|
||||
log.error("Compile server crashed", e);
|
||||
} finally {
|
||||
shutdownBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getChangedClasses(Collection<File> changedFiles) {
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
for (File file : changedFiles) {
|
||||
String path = file.getPath().replace('\\', '/');
|
||||
if (!path.endsWith(".class")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String prefix = Arrays.stream(classPath)
|
||||
.filter(path::startsWith)
|
||||
.findFirst()
|
||||
.orElse("");
|
||||
int start = prefix.length();
|
||||
if (start < path.length() && path.charAt(start) == '/') {
|
||||
++start;
|
||||
}
|
||||
|
||||
path = path.substring(start, path.length() - ".class".length()).replace('/', '.');
|
||||
result.add(path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void initBuilder() throws IOException {
|
||||
buildTarget = new IncrementalDirectoryBuildTarget(new File(targetPath));
|
||||
watcher = new FileSystemWatcher(classPath);
|
||||
|
||||
classSource = createCachedSource();
|
||||
astCache = new InMemoryMethodNodeCache(referenceCache, symbolTable, fileSymbolTable, variableSymbolTable);
|
||||
programCache = new InMemoryProgramCache(referenceCache, symbolTable, fileSymbolTable, variableSymbolTable);
|
||||
}
|
||||
|
||||
private void shutdownBuilder() {
|
||||
try {
|
||||
watcher.dispose();
|
||||
} catch (IOException e) {
|
||||
log.debug("Exception caught", e);
|
||||
}
|
||||
classSource = null;
|
||||
watcher = null;
|
||||
astCache = null;
|
||||
programCache = null;
|
||||
|
||||
log.info("Build thread complete");
|
||||
}
|
||||
|
||||
private MemoryCachedClassReaderSource createCachedSource() {
|
||||
return new MemoryCachedClassReaderSource(referenceCache, symbolTable, fileSymbolTable,
|
||||
variableSymbolTable);
|
||||
}
|
||||
|
||||
private void buildOnce() {
|
||||
fireBuildStarted();
|
||||
reportProgress(0);
|
||||
|
||||
ClassLoader classLoader = initClassLoader();
|
||||
ClasspathResourceReader reader = new ClasspathResourceReader(classLoader);
|
||||
ResourceClassHolderMapper rawMapper = new ResourceClassHolderMapper(reader, referenceCache);
|
||||
Function<String, ClassHolder> classPathMapper = new ClasspathResourceMapper(classLoader, referenceCache,
|
||||
rawMapper);
|
||||
classSource.setProvider(name -> PreOptimizingClassHolderSource.optimize(classPathMapper, name));
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
CTarget cTarget = new CTarget(stringPool);
|
||||
|
||||
TeaVM vm = new TeaVMBuilder(cTarget)
|
||||
.setReferenceCache(referenceCache)
|
||||
.setClassLoader(classLoader)
|
||||
.setClassSource(classSource)
|
||||
.setDependencyAnalyzerFactory(FastDependencyAnalyzer::new)
|
||||
.setClassSourcePacker(this::packClasses)
|
||||
.build();
|
||||
|
||||
cTarget.setIncremental(true);
|
||||
cTarget.setMinHeapSize(minHeapSize * 1024 * 1024);
|
||||
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
|
||||
vm.setCacheStatus(classSource);
|
||||
vm.addVirtualMethods(m -> true);
|
||||
vm.setProgressListener(progressListener);
|
||||
vm.setProgramCache(programCache);
|
||||
vm.installPlugins();
|
||||
|
||||
vm.setLastKnownClasses(lastReachedClasses);
|
||||
vm.entryPoint(mainClass);
|
||||
|
||||
log.info("Starting build");
|
||||
progressListener.last = 0;
|
||||
progressListener.lastTime = System.currentTimeMillis();
|
||||
vm.build(buildTarget, "");
|
||||
|
||||
postBuild(vm, startTime);
|
||||
}
|
||||
|
||||
private void postBuild(TeaVM vm, long startTime) {
|
||||
if (!vm.wasCancelled()) {
|
||||
log.info("Recompiled stale methods: " + programCache.getPendingItemsCount());
|
||||
fireBuildComplete(vm);
|
||||
if (vm.getProblemProvider().getSevereProblems().isEmpty()) {
|
||||
log.info("Build complete successfully");
|
||||
lastReachedClasses = vm.getDependencyInfo().getReachableClasses().size();
|
||||
classSource.commit();
|
||||
programCache.commit();
|
||||
astCache.commit();
|
||||
reportCompilationComplete(true);
|
||||
} else {
|
||||
log.info("Build complete with errors");
|
||||
reportCompilationComplete(false);
|
||||
}
|
||||
printStats(vm, startTime);
|
||||
TeaVMProblemRenderer.describeProblems(vm, log);
|
||||
} else {
|
||||
log.info("Build cancelled");
|
||||
fireBuildCancelled();
|
||||
}
|
||||
|
||||
astCache.discard();
|
||||
programCache.discard();
|
||||
buildTarget.reset();
|
||||
stringPool.reset();
|
||||
cancelRequested = false;
|
||||
}
|
||||
|
||||
private void printStats(TeaVM vm, long startTime) {
|
||||
if (vm.getWrittenClasses() != null) {
|
||||
int classCount = vm.getWrittenClasses().getClassNames().size();
|
||||
int methodCount = 0;
|
||||
for (String className : vm.getWrittenClasses().getClassNames()) {
|
||||
ClassReader cls = vm.getWrittenClasses().get(className);
|
||||
methodCount += cls.getMethods().size();
|
||||
}
|
||||
|
||||
log.info("Classes compiled: " + classCount);
|
||||
log.info("Methods compiled: " + methodCount);
|
||||
}
|
||||
|
||||
log.info("Compilation took " + (System.currentTimeMillis() - startTime) + " ms");
|
||||
}
|
||||
|
||||
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 ClassLoader initClassLoader() {
|
||||
URL[] urls = new URL[classPath.length];
|
||||
try {
|
||||
for (int i = 0; i < classPath.length; i++) {
|
||||
urls[i] = new File(classPath[i]).toURI().toURL();
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new URLClassLoader(urls, IncrementalCBuilder.class.getClassLoader());
|
||||
}
|
||||
|
||||
private void reportProgress(double progress) {
|
||||
synchronized (statusLock) {
|
||||
if (compiling && this.progress == progress) {
|
||||
return;
|
||||
}
|
||||
compiling = true;
|
||||
this.progress = progress;
|
||||
}
|
||||
|
||||
ProgressHandler[] handlers;
|
||||
synchronized (progressHandlers) {
|
||||
handlers = progressHandlers.toArray(new ProgressHandler[0]);
|
||||
}
|
||||
|
||||
for (ProgressHandler handler : handlers) {
|
||||
handler.progress(progress);
|
||||
}
|
||||
|
||||
for (BuilderListener listener : listeners) {
|
||||
listener.compilationProgress(progress);
|
||||
}
|
||||
}
|
||||
|
||||
private void reportCompilationComplete(boolean success) {
|
||||
synchronized (statusLock) {
|
||||
if (!compiling) {
|
||||
return;
|
||||
}
|
||||
compiling = false;
|
||||
}
|
||||
|
||||
ProgressHandler[] handlers;
|
||||
synchronized (progressHandlers) {
|
||||
handlers = progressHandlers.toArray(new ProgressHandler[0]);
|
||||
}
|
||||
|
||||
for (ProgressHandler handler : handlers) {
|
||||
handler.complete(success);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireBuildStarted() {
|
||||
for (BuilderListener listener : listeners) {
|
||||
listener.compilationStarted();
|
||||
}
|
||||
}
|
||||
|
||||
private void fireBuildCancelled() {
|
||||
for (BuilderListener listener : listeners) {
|
||||
listener.compilationCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
private void fireBuildComplete(TeaVM vm) {
|
||||
SimpleBuildResult result = new SimpleBuildResult(vm, Collections.emptyList());
|
||||
for (BuilderListener listener : listeners) {
|
||||
listener.compilationComplete(result);
|
||||
}
|
||||
}
|
||||
|
||||
private final ProgressListenerImpl progressListener = new ProgressListenerImpl();
|
||||
|
||||
class ProgressListenerImpl implements TeaVMProgressListener {
|
||||
private int start;
|
||||
private int end;
|
||||
private int phaseLimit;
|
||||
private int last;
|
||||
private long lastTime;
|
||||
|
||||
@Override
|
||||
public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) {
|
||||
switch (phase) {
|
||||
case DEPENDENCY_ANALYSIS:
|
||||
start = 0;
|
||||
end = 500;
|
||||
break;
|
||||
case COMPILING:
|
||||
start = 500;
|
||||
end = 1000;
|
||||
break;
|
||||
}
|
||||
phaseLimit = count;
|
||||
return progressReached(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeaVMProgressFeedback progressReached(int progress) {
|
||||
int current = start + Math.min(progress, phaseLimit) * (end - start) / phaseLimit;
|
||||
if (current != last) {
|
||||
if (current - last > 10 || System.currentTimeMillis() - lastTime > 100) {
|
||||
lastTime = System.currentTimeMillis();
|
||||
last = current;
|
||||
reportProgress(current / 10.0);
|
||||
}
|
||||
}
|
||||
return getResult();
|
||||
}
|
||||
|
||||
private TeaVMProgressFeedback getResult() {
|
||||
if (cancelRequested) {
|
||||
log.info("Trying to cancel compilation due to user request");
|
||||
return TeaVMProgressFeedback.CANCEL;
|
||||
}
|
||||
|
||||
if (stopped) {
|
||||
log.info("Trying to cancel compilation due to server stopping");
|
||||
return TeaVMProgressFeedback.CANCEL;
|
||||
}
|
||||
|
||||
try {
|
||||
if (watcher.hasChanges()) {
|
||||
log.info("Changes detected, cancelling build");
|
||||
return TeaVMProgressFeedback.CANCEL;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.info("IO error occurred", e);
|
||||
return TeaVMProgressFeedback.CANCEL;
|
||||
}
|
||||
|
||||
return TeaVMProgressFeedback.CONTINUE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.tooling.c.incremental;
|
||||
|
||||
public interface ProgressHandler {
|
||||
void complete(boolean success);
|
||||
|
||||
void progress(double value);
|
||||
}
|
|
@ -56,6 +56,11 @@
|
|||
<artifactId>teavm-devserver</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-c-incremental</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-jso-impl</artifactId>
|
||||
|
|
139
tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java
Normal file
139
tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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.cli;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.OptionBuilder;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.cli.PosixParser;
|
||||
import org.teavm.tooling.ConsoleTeaVMToolLog;
|
||||
import org.teavm.tooling.c.incremental.IncrementalCBuilder;
|
||||
|
||||
public class TeaVMCBuilderRunner {
|
||||
private static Options options = new Options();
|
||||
private IncrementalCBuilder builder;
|
||||
private CommandLine commandLine;
|
||||
|
||||
static {
|
||||
setupOptions();
|
||||
}
|
||||
|
||||
@SuppressWarnings("static-access")
|
||||
private static void setupOptions() {
|
||||
options.addOption(OptionBuilder
|
||||
.withArgName("directory")
|
||||
.hasArg()
|
||||
.withDescription("a directory in which generated C files will be placed")
|
||||
.withLongOpt("targetdir")
|
||||
.create('d'));
|
||||
options.addOption(OptionBuilder
|
||||
.withArgName("classpath")
|
||||
.hasArgs()
|
||||
.withDescription("classpath element (either directory or jar file)")
|
||||
.withLongOpt("classpath")
|
||||
.create('p'));
|
||||
options.addOption(OptionBuilder
|
||||
.withDescription("display more messages on server log")
|
||||
.withLongOpt("verbose")
|
||||
.create('v'));
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("min-heap")
|
||||
.withArgName("size")
|
||||
.hasArg()
|
||||
.withDescription("Minimum heap size in bytes")
|
||||
.create());
|
||||
}
|
||||
|
||||
private TeaVMCBuilderRunner(CommandLine commandLine) {
|
||||
this.commandLine = commandLine;
|
||||
builder = new IncrementalCBuilder();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 0) {
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
CommandLineParser parser = new PosixParser();
|
||||
CommandLine commandLine;
|
||||
try {
|
||||
commandLine = parser.parse(options, args);
|
||||
} catch (ParseException e) {
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
TeaVMCBuilderRunner runner = new TeaVMCBuilderRunner(commandLine);
|
||||
runner.parseArguments();
|
||||
runner.runAll();
|
||||
}
|
||||
|
||||
private void parseArguments() {
|
||||
parseClassPathOptions();
|
||||
parseOutputOptions();
|
||||
parseHeap();
|
||||
|
||||
builder.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v')));
|
||||
|
||||
String[] args = commandLine.getArgs();
|
||||
if (args.length != 1) {
|
||||
System.err.println("Unexpected arguments");
|
||||
printUsage();
|
||||
} else if (args.length == 1) {
|
||||
builder.setMainClass(args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseOutputOptions() {
|
||||
if (commandLine.hasOption("d")) {
|
||||
builder.setTargetPath(commandLine.getOptionValue("d"));
|
||||
}
|
||||
}
|
||||
|
||||
private void parseClassPathOptions() {
|
||||
if (commandLine.hasOption('p')) {
|
||||
builder.setClassPath(commandLine.getOptionValues('p'));
|
||||
}
|
||||
}
|
||||
|
||||
private void parseHeap() {
|
||||
if (commandLine.hasOption("min-heap")) {
|
||||
int size;
|
||||
try {
|
||||
size = Integer.parseInt(commandLine.getOptionValue("min-heap"));
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.print("Wrong heap size");
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
builder.setMinHeapSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
private void runAll() {
|
||||
builder.start();
|
||||
}
|
||||
|
||||
private static void printUsage() {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
formatter.printHelp("java " + TeaVMCBuilderRunner.class.getName() + " [OPTIONS] [qualified.main.Class]",
|
||||
options);
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -13,22 +13,21 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.devserver;
|
||||
package org.teavm.tooling.builder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.teavm.callgraph.CallGraph;
|
||||
import org.teavm.diagnostics.ProblemProvider;
|
||||
import org.teavm.tooling.InstructionLocationReader;
|
||||
import org.teavm.tooling.builder.BuildResult;
|
||||
import org.teavm.vm.TeaVM;
|
||||
|
||||
class CodeServletBuildResult implements BuildResult {
|
||||
public class SimpleBuildResult implements BuildResult {
|
||||
private TeaVM vm;
|
||||
private List<String> generatedFiles;
|
||||
private Collection<String> usedResources;
|
||||
|
||||
public CodeServletBuildResult(TeaVM vm, List<String> generatedFiles) {
|
||||
public SimpleBuildResult(TeaVM vm, List<String> generatedFiles) {
|
||||
this.vm = vm;
|
||||
this.generatedFiles = generatedFiles;
|
||||
}
|
|
@ -86,6 +86,7 @@ import org.teavm.parsing.resource.ResourceClassHolderMapper;
|
|||
import org.teavm.tooling.EmptyTeaVMToolLog;
|
||||
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||
import org.teavm.tooling.TeaVMToolLog;
|
||||
import org.teavm.tooling.builder.SimpleBuildResult;
|
||||
import org.teavm.tooling.util.FileSystemWatcher;
|
||||
import org.teavm.vm.MemoryBuildTarget;
|
||||
import org.teavm.vm.TeaVM;
|
||||
|
@ -995,7 +996,7 @@ public class CodeServlet extends HttpServlet {
|
|||
}
|
||||
|
||||
private void fireBuildComplete(TeaVM vm) {
|
||||
CodeServletBuildResult result = new CodeServletBuildResult(vm, new ArrayList<>(buildTarget.getNames()));
|
||||
SimpleBuildResult result = new SimpleBuildResult(vm, new ArrayList<>(buildTarget.getNames()));
|
||||
for (DevServerListener listener : listeners) {
|
||||
listener.compilationComplete(result);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user