mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-03 05:44:10 -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.ObjectByteHashMap;
|
||||||
import com.carrotsearch.hppc.ObjectByteMap;
|
import com.carrotsearch.hppc.ObjectByteMap;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashSet;
|
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.NameProvider;
|
||||||
import org.teavm.backend.c.generate.OutputFileUtil;
|
import org.teavm.backend.c.generate.OutputFileUtil;
|
||||||
import org.teavm.backend.c.generate.SimpleIncludeManager;
|
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.StringPool;
|
||||||
import org.teavm.backend.c.generate.StringPoolGenerator;
|
import org.teavm.backend.c.generate.StringPoolGenerator;
|
||||||
import org.teavm.backend.c.generators.ArrayGenerator;
|
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.CloneArrayInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||||
import org.teavm.model.lowlevel.Characteristics;
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||||
|
@ -141,12 +141,27 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
private int minHeapSize = 32 * 1024 * 1024;
|
private int minHeapSize = 32 * 1024 * 1024;
|
||||||
private List<IntrinsicFactory> intrinsicFactories = new ArrayList<>();
|
private List<IntrinsicFactory> intrinsicFactories = new ArrayList<>();
|
||||||
private List<GeneratorFactory> generatorFactories = new ArrayList<>();
|
private List<GeneratorFactory> generatorFactories = new ArrayList<>();
|
||||||
|
private Characteristics characteristics;
|
||||||
private Set<MethodReference> asyncMethods;
|
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) {
|
public void setMinHeapSize(int minHeapSize) {
|
||||||
this.minHeapSize = minHeapSize;
|
this.minHeapSize = minHeapSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setIncremental(boolean incremental) {
|
||||||
|
this.incremental = incremental;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ClassHolderTransformer> getTransformers() {
|
public List<ClassHolderTransformer> getTransformers() {
|
||||||
List<ClassHolderTransformer> transformers = new ArrayList<>();
|
List<ClassHolderTransformer> transformers = new ArrayList<>();
|
||||||
|
@ -163,7 +178,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
@Override
|
@Override
|
||||||
public void setController(TeaVMTargetController controller) {
|
public void setController(TeaVMTargetController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||||
classInitializerTransformer = new ClassInitializerTransformer();
|
classInitializerTransformer = new ClassInitializerTransformer();
|
||||||
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
||||||
|
@ -273,6 +288,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
nullCheckTransformation.apply(program, method.getResultType());
|
nullCheckTransformation.apply(program, method.getResultType());
|
||||||
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods)
|
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods)
|
||||||
.apply(program, method.getReference());
|
.apply(program, method.getReference());
|
||||||
|
ShadowStackTransformer shadowStackTransformer = !incremental
|
||||||
|
? this.shadowStackTransformer
|
||||||
|
: new ShadowStackTransformer(characteristics);
|
||||||
shadowStackTransformer.apply(program, method);
|
shadowStackTransformer.apply(program, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +299,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
|
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
|
||||||
ClassHierarchy hierarchy = new ClassHierarchy(classes);
|
ClassHierarchy hierarchy = new ClassHierarchy(classes);
|
||||||
TagRegistry tagRegistry = new TagRegistry(classes, hierarchy);
|
TagRegistry tagRegistry = new TagRegistry(classes, hierarchy);
|
||||||
StringPool stringPool = new StringPool();
|
|
||||||
|
|
||||||
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true);
|
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true);
|
||||||
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||||
|
@ -313,12 +330,18 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
|
|
||||||
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
|
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
|
||||||
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
|
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
|
||||||
intrinsics, generators, asyncMethods::contains, buildTarget);
|
intrinsics, generators, asyncMethods::contains, buildTarget, incremental);
|
||||||
|
|
||||||
BufferedCodeWriter runtimeWriter = new BufferedCodeWriter();
|
BufferedCodeWriter runtimeWriter = new BufferedCodeWriter();
|
||||||
copyResource("runtime.h", "runtime.h", buildTarget);
|
BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter();
|
||||||
emitResource(runtimeWriter, "runtime.c");
|
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(),
|
ClassGenerator classGenerator = new ClassGenerator(context, controller.getUnprocessedClassSource(),
|
||||||
tagRegistry, decompiler);
|
tagRegistry, decompiler);
|
||||||
IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl(
|
IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl(
|
||||||
|
@ -334,8 +357,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
generateClasses(classes, classGenerator, buildTarget);
|
generateClasses(classes, classGenerator, buildTarget);
|
||||||
generateSpecialFunctions(context, runtimeWriter);
|
generateSpecialFunctions(context, runtimeWriter);
|
||||||
OutputFileUtil.write(runtimeWriter, "runtime.c", buildTarget);
|
OutputFileUtil.write(runtimeWriter, "runtime.c", buildTarget);
|
||||||
|
OutputFileUtil.write(runtimeHeaderWriter, "runtime.h", buildTarget);
|
||||||
|
|
||||||
generateCallSites(buildTarget, context);
|
generateCallSites(buildTarget, context, classes.getClassNames());
|
||||||
generateStrings(buildTarget, stringPool);
|
generateStrings(buildTarget, stringPool);
|
||||||
|
|
||||||
List<ValueType> types = classGenerator.getTypes().stream()
|
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,
|
private void generateClasses(ListableClassHolderSource classes, ClassGenerator classGenerator,
|
||||||
BuildTarget buildTarget) throws IOException {
|
BuildTarget buildTarget) throws IOException {
|
||||||
List<String> classNames = sortClassNames(classes);
|
List<String> classNames = sortClassNames(classes);
|
||||||
|
@ -444,7 +449,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
return classNames;
|
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 writer = new BufferedCodeWriter();
|
||||||
BufferedCodeWriter headerWriter = new BufferedCodeWriter();
|
BufferedCodeWriter headerWriter = new BufferedCodeWriter();
|
||||||
|
|
||||||
|
@ -458,15 +464,32 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
headerIncludes.includePath("runtime.h");
|
headerIncludes.includePath("runtime.h");
|
||||||
headerIncludes.includeClass(CallSiteGenerator.CALL_SITE);
|
headerIncludes.includeClass(CallSiteGenerator.CALL_SITE);
|
||||||
|
|
||||||
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
|
if (incremental) {
|
||||||
headerWriter.println("extern " + callSiteName + " teavm_callSites[];");
|
generateIncrementalCallSites(context, headerWriter);
|
||||||
|
} else {
|
||||||
new CallSiteGenerator(context, writer, includes).generate(shadowStackTransformer.getCallSites());
|
generateFastCallSites(context, writer, includes, headerWriter, classNames);
|
||||||
|
}
|
||||||
|
|
||||||
OutputFileUtil.write(writer, "callsites.c", buildTarget);
|
OutputFileUtil.write(writer, "callsites.c", buildTarget);
|
||||||
OutputFileUtil.write(headerWriter, "callsites.h", 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 {
|
private void generateStrings(BuildTarget buildTarget, StringPool stringPool) throws IOException {
|
||||||
BufferedCodeWriter writer = new BufferedCodeWriter();
|
BufferedCodeWriter writer = new BufferedCodeWriter();
|
||||||
BufferedCodeWriter headerWriter = new BufferedCodeWriter();
|
BufferedCodeWriter headerWriter = new BufferedCodeWriter();
|
||||||
|
@ -685,7 +708,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
|
|
||||||
int size = context.getStringPool().getStrings().size();
|
int size = context.getStringPool().getStrings().size();
|
||||||
writer.println("for (int i = 0; i < " + size + "; ++i) {").indent();
|
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("}");
|
writer.outdent().println("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,16 +38,24 @@ public class CallSiteGenerator {
|
||||||
private List<ExceptionHandlerDescriptor> exceptionHandlers = new ArrayList<>();
|
private List<ExceptionHandlerDescriptor> exceptionHandlers = new ArrayList<>();
|
||||||
private String callSiteLocationName;
|
private String callSiteLocationName;
|
||||||
private String exceptionHandlerName;
|
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.context = context;
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
this.includes = includes;
|
this.includes = includes;
|
||||||
callSiteLocationName = context.getNames().forClass(CALL_SITE_LOCATION);
|
callSiteLocationName = context.getNames().forClass(CALL_SITE_LOCATION);
|
||||||
exceptionHandlerName = context.getNames().forClass(EXCEPTION_HANDLER);
|
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();
|
CodeWriter writerForLocations = writer.fragment();
|
||||||
generateCallSites(callSites);
|
generateCallSites(callSites);
|
||||||
|
|
||||||
|
@ -58,12 +66,15 @@ public class CallSiteGenerator {
|
||||||
writer = oldWriter;
|
writer = oldWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateCallSites(List<CallSiteDescriptor> callSites) {
|
private void generateCallSites(List<? extends CallSiteDescriptor> callSites) {
|
||||||
String callSiteName = context.getNames().forClass(CALL_SITE);
|
String callSiteName = context.getNames().forClass(CALL_SITE);
|
||||||
|
|
||||||
includes.includeClass(CALL_SITE);
|
includes.includeClass(CALL_SITE);
|
||||||
includes.includePath("strings.h");
|
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 handlerCountName = fieldName(CALL_SITE, "handlerCount");
|
||||||
String firstHandlerName = fieldName(CALL_SITE, "firstHandler");
|
String firstHandlerName = fieldName(CALL_SITE, "firstHandler");
|
||||||
String locationName = fieldName(CALL_SITE, "location");
|
String locationName = fieldName(CALL_SITE, "location");
|
||||||
|
@ -86,14 +97,14 @@ public class CallSiteGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
String firstHandlerExpr = !callSite.getHandlers().isEmpty()
|
String firstHandlerExpr = !callSite.getHandlers().isEmpty()
|
||||||
? "teavm_exceptionHandlers + " + exceptionHandlers.size()
|
? "exceptionHandlers_" + callSitesName + " + " + exceptionHandlers.size()
|
||||||
: "NULL";
|
: "NULL";
|
||||||
writer.println().print("{ ");
|
writer.println().print("{ ");
|
||||||
writer.print(".").print(handlerCountName).print(" = ")
|
writer.print(".").print(handlerCountName).print(" = ")
|
||||||
.print(String.valueOf(callSite.getHandlers().size())).print(", ");
|
.print(String.valueOf(callSite.getHandlers().size())).print(", ");
|
||||||
writer.print(".").print(firstHandlerName).print(" = ").print(firstHandlerExpr).print(", ");
|
writer.print(".").print(firstHandlerName).print(" = ").print(firstHandlerExpr).print(", ");
|
||||||
writer.print(".").print(locationName).print(" = ")
|
writer.print(".").print(locationName).print(" = ")
|
||||||
.print(locationIndex >= 0 ? "teavm_callSiteLocations + " + locationIndex : "NULL");
|
.print(locationIndex >= 0 ? "callSiteLocations_" + callSitesName + " + " + locationIndex : "NULL");
|
||||||
writer.print(" }");
|
writer.print(" }");
|
||||||
|
|
||||||
exceptionHandlers.addAll(callSite.getHandlers());
|
exceptionHandlers.addAll(callSite.getHandlers());
|
||||||
|
@ -104,8 +115,8 @@ public class CallSiteGenerator {
|
||||||
|
|
||||||
private void generateLocations() {
|
private void generateLocations() {
|
||||||
includes.includeClass(CALL_SITE_LOCATION);
|
includes.includeClass(CALL_SITE_LOCATION);
|
||||||
writer.print("static ").print(callSiteLocationName).print(" teavm_callSiteLocations[" + locations.size()
|
writer.print("static ").print(callSiteLocationName).print(" callSiteLocations_" + callSitesName
|
||||||
+ "] = {").indent();
|
+ "[" + locations.size() + "] = {").indent();
|
||||||
|
|
||||||
String fileNameName = fieldName(CALL_SITE_LOCATION, "fileName");
|
String fileNameName = fieldName(CALL_SITE_LOCATION, "fileName");
|
||||||
String classNameName = fieldName(CALL_SITE_LOCATION, "className");
|
String classNameName = fieldName(CALL_SITE_LOCATION, "className");
|
||||||
|
@ -137,7 +148,7 @@ public class CallSiteGenerator {
|
||||||
|
|
||||||
private void generateHandlers() {
|
private void generateHandlers() {
|
||||||
includes.includeClass(EXCEPTION_HANDLER);
|
includes.includeClass(EXCEPTION_HANDLER);
|
||||||
writer.print("static ").print(exceptionHandlerName).print(" teavm_exceptionHandlers["
|
writer.print("static ").print(exceptionHandlerName).print(" exceptionHandlers_" + callSitesName + "["
|
||||||
+ exceptionHandlers.size() + "] = {").indent();
|
+ exceptionHandlers.size() + "] = {").indent();
|
||||||
|
|
||||||
String idName = fieldName(EXCEPTION_HANDLER, "id");
|
String idName = fieldName(EXCEPTION_HANDLER, "id");
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.backend.c.generate;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
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.ConstructMultiArrayInstruction;
|
||||||
import org.teavm.model.instructions.InstructionVisitor;
|
import org.teavm.model.instructions.InstructionVisitor;
|
||||||
import org.teavm.model.instructions.StringConstantInstruction;
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
|
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||||
import org.teavm.model.lowlevel.Characteristics;
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
|
import org.teavm.runtime.CallSite;
|
||||||
import org.teavm.runtime.RuntimeArray;
|
import org.teavm.runtime.RuntimeArray;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
import org.teavm.runtime.RuntimeObject;
|
import org.teavm.runtime.RuntimeObject;
|
||||||
|
@ -81,6 +84,7 @@ public class ClassGenerator {
|
||||||
private CodeWriter headerWriter;
|
private CodeWriter headerWriter;
|
||||||
private IncludeManager includes;
|
private IncludeManager includes;
|
||||||
private IncludeManager headerIncludes;
|
private IncludeManager headerIncludes;
|
||||||
|
private boolean stackDefined;
|
||||||
|
|
||||||
public ClassGenerator(GenerationContext context, ClassReaderSource unprocessedClassSource,
|
public ClassGenerator(GenerationContext context, ClassReaderSource unprocessedClassSource,
|
||||||
TagRegistry tagRegistry, Decompiler decompiler) {
|
TagRegistry tagRegistry, Decompiler decompiler) {
|
||||||
|
@ -161,7 +165,9 @@ public class ClassGenerator {
|
||||||
};
|
};
|
||||||
|
|
||||||
public void generateClass(CodeWriter writer, CodeWriter headerWriter, ClassHolder cls) {
|
public void generateClass(CodeWriter writer, CodeWriter headerWriter, ClassHolder cls) {
|
||||||
|
stackDefined = false;
|
||||||
init(writer, headerWriter, fileName(cls.getName()));
|
init(writer, headerWriter, fileName(cls.getName()));
|
||||||
|
|
||||||
codeGenerator = new CodeGenerator(context, codeWriter, includes);
|
codeGenerator = new CodeGenerator(context, codeWriter, includes);
|
||||||
|
|
||||||
String sysInitializerName = context.getNames().forClassSystemInitializer(cls.getName());
|
String sysInitializerName = context.getNames().forClassSystemInitializer(cls.getName());
|
||||||
|
@ -180,6 +186,12 @@ public class ClassGenerator {
|
||||||
generateLayoutArray(cls.getName());
|
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) {
|
public void generateType(CodeWriter writer, CodeWriter headerWriter, ValueType type) {
|
||||||
init(writer, headerWriter, fileName(type));
|
init(writer, headerWriter, fileName(type));
|
||||||
includes.includeType(type);
|
includes.includeType(type);
|
||||||
|
@ -220,6 +232,24 @@ public class ClassGenerator {
|
||||||
continue;
|
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);
|
generateMethodForwardDeclaration(method);
|
||||||
RegularMethodNode methodNode = decompiler.decompileRegular(method);
|
RegularMethodNode methodNode = decompiler.decompileRegular(method);
|
||||||
codeGenerator.generateMethod(methodNode);
|
codeGenerator.generateMethod(methodNode);
|
||||||
|
@ -454,6 +484,8 @@ public class ClassGenerator {
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
String layout = "NULL";
|
String layout = "NULL";
|
||||||
String initFunction = "NULL";
|
String initFunction = "NULL";
|
||||||
|
String superinterfaceCount = "0";
|
||||||
|
String superinterfaces = "NULL";
|
||||||
|
|
||||||
if (type instanceof ValueType.Object) {
|
if (type instanceof ValueType.Object) {
|
||||||
String className = ((ValueType.Object) type).getClassName();
|
String className = ((ValueType.Object) type).getClassName();
|
||||||
|
@ -473,7 +505,7 @@ public class ClassGenerator {
|
||||||
flags |= RuntimeClass.ENUM;
|
flags |= RuntimeClass.ENUM;
|
||||||
}
|
}
|
||||||
List<TagRegistry.Range> ranges = tagRegistry.getRanges(className);
|
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()))) {
|
if (cls != null && cls.getParent() != null && types.contains(ValueType.object(cls.getParent()))) {
|
||||||
includes.includeClass(cls.getParent());
|
includes.includeClass(cls.getParent());
|
||||||
|
@ -487,10 +519,31 @@ public class ClassGenerator {
|
||||||
if (cls != null && needsInitializer(cls)) {
|
if (cls != null && needsInitializer(cls)) {
|
||||||
initFunction = context.getNames().forClassInitializer(className);
|
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) {
|
} else if (type instanceof ValueType.Array) {
|
||||||
includes.includeClass("java.lang.Object");
|
includes.includeClass("java.lang.Object");
|
||||||
parent = "(TeaVM_Class*) &" + context.getNames().forClassInstance(ValueType.object("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();
|
ValueType itemType = ((ValueType.Array) type).getItemType();
|
||||||
sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(itemType) + ")";
|
sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(itemType) + ")";
|
||||||
includes.includeType(itemType);
|
includes.includeType(itemType);
|
||||||
|
@ -532,6 +585,8 @@ public class ClassGenerator {
|
||||||
codeWriter.println(".itemType = " + itemTypeExpr + ",");
|
codeWriter.println(".itemType = " + itemTypeExpr + ",");
|
||||||
codeWriter.println(".isSupertypeOf = &" + superTypeFunction + ",");
|
codeWriter.println(".isSupertypeOf = &" + superTypeFunction + ",");
|
||||||
codeWriter.println(".superclass = " + parent + ",");
|
codeWriter.println(".superclass = " + parent + ",");
|
||||||
|
codeWriter.println(".superinterfaceCount = " + superinterfaceCount + ",");
|
||||||
|
codeWriter.println(".superinterfaces = " + superinterfaces + ",");
|
||||||
codeWriter.println(".enumValues = NULL,");
|
codeWriter.println(".enumValues = NULL,");
|
||||||
codeWriter.println(".layout = " + layout + ",");
|
codeWriter.println(".layout = " + layout + ",");
|
||||||
codeWriter.println(".enumValues = " + enumConstants + ",");
|
codeWriter.println(".enumValues = " + enumConstants + ",");
|
||||||
|
@ -566,6 +621,7 @@ public class ClassGenerator {
|
||||||
if (type instanceof ValueType.Object) {
|
if (type instanceof ValueType.Object) {
|
||||||
String className = ((ValueType.Object) type).getClassName();
|
String className = ((ValueType.Object) type).getClassName();
|
||||||
return !context.getCharacteristics().isStructure(className)
|
return !context.getCharacteristics().isStructure(className)
|
||||||
|
&& !context.getCharacteristics().isFunction(className)
|
||||||
&& !className.equals(Address.class.getName());
|
&& !className.equals(Address.class.getName());
|
||||||
} else {
|
} else {
|
||||||
return type instanceof ValueType.Array;
|
return type instanceof ValueType.Array;
|
||||||
|
@ -766,6 +822,14 @@ public class ClassGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateIsSuperclassFunction(String className) {
|
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);
|
List<TagRegistry.Range> ranges = tagRegistry.getRanges(className);
|
||||||
if (ranges.isEmpty()) {
|
if (ranges.isEmpty()) {
|
||||||
codeWriter.println("return INT32_C(0);");
|
codeWriter.println("return INT32_C(0);");
|
||||||
|
@ -789,6 +853,25 @@ public class ClassGenerator {
|
||||||
codeWriter.println("return INT32_C(1);");
|
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) {
|
private void generateIsSuperArrayFunction(ValueType itemType) {
|
||||||
String itemTypeName = context.getNames().forMemberField(new FieldReference(
|
String itemTypeName = context.getNames().forMemberField(new FieldReference(
|
||||||
RuntimeClass.class.getName(), "itemType"));
|
RuntimeClass.class.getName(), "itemType"));
|
||||||
|
|
|
@ -517,6 +517,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
writer.print("TEAVM_ARRAY_DATA(TEAVM_FIELD(");
|
writer.print("TEAVM_ARRAY_DATA(TEAVM_FIELD(");
|
||||||
String typeName = ((ValueType.Object) type).getClassName();
|
String typeName = ((ValueType.Object) type).getClassName();
|
||||||
expr.getArguments().get(i).acceptVisitor(this);
|
expr.getArguments().get(i).acceptVisitor(this);
|
||||||
|
includes.includeClass(typeName);
|
||||||
writer.print(", ").print(names.forClass(typeName)).print(", ")
|
writer.print(", ").print(names.forClass(typeName)).print(", ")
|
||||||
.print(names.forMemberField(new FieldReference(typeName, "array"))).print(")");
|
.print(names.forMemberField(new FieldReference(typeName, "array"))).print(")");
|
||||||
writer.print(", ").print(BUFFER_TYPES.get(typeName)).print(")");
|
writer.print(", ").print(BUFFER_TYPES.get(typeName)).print(")");
|
||||||
|
@ -1016,6 +1017,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
ClassGenerator.escape(name, sb);
|
ClassGenerator.escape(name, sb);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isIncremental() {
|
||||||
|
return context.isIncremental();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static CVariableType typeToCType(ValueType type) {
|
private static CVariableType typeToCType(ValueType type) {
|
||||||
|
|
|
@ -43,11 +43,12 @@ public class GenerationContext {
|
||||||
private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>();
|
private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>();
|
||||||
private Predicate<MethodReference> asyncMethods;
|
private Predicate<MethodReference> asyncMethods;
|
||||||
private BuildTarget buildTarget;
|
private BuildTarget buildTarget;
|
||||||
|
private boolean incremental;
|
||||||
|
|
||||||
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
|
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
|
||||||
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
|
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
|
||||||
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators,
|
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators,
|
||||||
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget) {
|
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget, boolean incremental) {
|
||||||
this.virtualTableProvider = virtualTableProvider;
|
this.virtualTableProvider = virtualTableProvider;
|
||||||
this.characteristics = characteristics;
|
this.characteristics = characteristics;
|
||||||
this.dependencies = dependencies;
|
this.dependencies = dependencies;
|
||||||
|
@ -59,6 +60,7 @@ public class GenerationContext {
|
||||||
this.generators = new ArrayList<>(generators);
|
this.generators = new ArrayList<>(generators);
|
||||||
this.asyncMethods = asyncMethods;
|
this.asyncMethods = asyncMethods;
|
||||||
this.buildTarget = buildTarget;
|
this.buildTarget = buildTarget;
|
||||||
|
this.incremental = incremental;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addIntrinsic(Intrinsic intrinsic) {
|
public void addIntrinsic(Intrinsic intrinsic) {
|
||||||
|
@ -118,4 +120,8 @@ public class GenerationContext {
|
||||||
public BuildTarget getBuildTarget() {
|
public BuildTarget getBuildTarget() {
|
||||||
return buildTarget;
|
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");
|
memberFieldNames.put(new FieldReference(String.class.getName(), "hashCode"), "hashCode");
|
||||||
|
|
||||||
for (String name : new String[] { "size", "flags", "tag", "canary", "name", "itemType", "arrayType",
|
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(), name), name);
|
||||||
}
|
}
|
||||||
memberFieldNames.put(new FieldReference(RuntimeClass.class.getName(), "parent"), "superclass");
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -15,28 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.c.generate;
|
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;
|
import java.util.List;
|
||||||
|
|
||||||
public class StringPool {
|
public interface StringPool {
|
||||||
private final ObjectIntMap<String> stringIndexes = new ObjectIntHashMap<>();
|
int getStringIndex(String string);
|
||||||
private final List<String> strings = new ArrayList<>();
|
|
||||||
private final List<String> readonlyStrings = Collections.unmodifiableList(strings);
|
|
||||||
|
|
||||||
public int getStringIndex(String string) {
|
List<String> getStrings();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,9 @@ public class StringPoolGenerator {
|
||||||
writer.println("TeaVM_String teavm_stringPool[" + strings.size() + "] = {").indent();
|
writer.println("TeaVM_String teavm_stringPool[" + strings.size() + "] = {").indent();
|
||||||
for (int i = 0; i < strings.size(); ++i) {
|
for (int i = 0; i < strings.size(); ++i) {
|
||||||
String s = strings.get(i);
|
String s = strings.get(i);
|
||||||
|
if (s == null) {
|
||||||
|
writer.println("TEAVM_NULL_STRING");
|
||||||
|
} else {
|
||||||
boolean codes = hasBadCharacters(s);
|
boolean codes = hasBadCharacters(s);
|
||||||
String macroName = codes ? "TEAVM_STRING_FROM_CODES" : "TEAVM_STRING";
|
String macroName = codes ? "TEAVM_STRING_FROM_CODES" : "TEAVM_STRING";
|
||||||
writer.print(macroName + "(" + s.length() + ", " + s.hashCode() + ",");
|
writer.print(macroName + "(" + s.length() + ", " + s.hashCode() + ",");
|
||||||
|
@ -38,6 +41,7 @@ public class StringPoolGenerator {
|
||||||
generateSimpleStringLiteral(writer, s);
|
generateSimpleStringLiteral(writer, s);
|
||||||
}
|
}
|
||||||
writer.print(")");
|
writer.print(")");
|
||||||
|
}
|
||||||
|
|
||||||
writer.print(i < strings.size() - 1 ? "," : " ");
|
writer.print(i < strings.size() - 1 ? "," : " ");
|
||||||
writer.print(" // string #" + i);
|
writer.print(" // string #" + i);
|
||||||
|
|
|
@ -39,8 +39,10 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
|
||||||
switch (invocation.getMethod().getName()) {
|
switch (invocation.getMethod().getName()) {
|
||||||
case "findCallSiteById":
|
case "findCallSiteById":
|
||||||
context.includes().includePath("callsites.h");
|
context.includes().includePath("callsites.h");
|
||||||
context.writer().print("(teavm_callSites + ");
|
context.writer().print("TEAVM_FIND_CALLSITE(");
|
||||||
context.emit(invocation.getArguments().get(0));
|
context.emit(invocation.getArguments().get(0));
|
||||||
|
context.writer().print(", ");
|
||||||
|
context.emit(invocation.getArguments().get(1));
|
||||||
context.writer().print(")");
|
context.writer().print(")");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,4 +39,6 @@ public interface IntrinsicContext {
|
||||||
IncludeManager includes();
|
IncludeManager includes();
|
||||||
|
|
||||||
String escapeFileName(String name);
|
String escapeFileName(String name);
|
||||||
|
|
||||||
|
boolean isIncremental();
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,15 +78,16 @@ public class ShadowStackIntrinsic implements Intrinsic {
|
||||||
context.writer().print(")[0]");
|
context.writer().print(")[0]");
|
||||||
return;
|
return;
|
||||||
case "getStackRootCount":
|
case "getStackRootCount":
|
||||||
context.writer().print("((int32_t) (intptr_t) ((void**) ");
|
context.writer().print("TEAVM_GC_ROOTS_COUNT(");
|
||||||
context.emit(invocation.getArguments().get(0));
|
context.emit(invocation.getArguments().get(0));
|
||||||
context.writer().print(")[2])");
|
context.writer().print(")");
|
||||||
return;
|
return;
|
||||||
case "getStackRootPointer":
|
case "getStackRootPointer": {
|
||||||
context.writer().print("&((void**) ");
|
context.writer().print("TEAVM_GET_GC_ROOTS(");
|
||||||
context.emit(invocation.getArguments().get(0));
|
context.emit(invocation.getArguments().get(0));
|
||||||
context.writer().print(")[3]");
|
context.writer().print(")");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
case "getCallSiteId":
|
case "getCallSiteId":
|
||||||
context.writer().print("((int32_t) (intptr_t) ((void**) ");
|
context.writer().print("((int32_t) (intptr_t) ((void**) ");
|
||||||
context.emit(invocation.getArguments().get(0));
|
context.emit(invocation.getArguments().get(0));
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class ExceptionHandlingDependencyListener extends AbstractDependencyListe
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
if (method.getReference().equals(FILL_IN_STACK_TRACE)) {
|
if (method.getReference().equals(FILL_IN_STACK_TRACE)) {
|
||||||
DependencyNode node = agent.linkField(STACK_TRACE).getValue();
|
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"));
|
node.getArrayItem().propagate(agent.getType("java.lang.StackTraceElement"));
|
||||||
|
|
||||||
MethodDependency initElem = agent.linkMethod(STACK_TRACE_ELEMENT_INIT);
|
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.CloneArrayInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||||
import org.teavm.model.lowlevel.Characteristics;
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||||
|
@ -374,7 +375,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
int pages = (minHeapSize + pageSize - 1) / pageSize;
|
int pages = (minHeapSize + pageSize - 1) / pageSize;
|
||||||
module.setMemorySize(pages);
|
module.setMemorySize(pages);
|
||||||
generateMethods(classes, context, generator, classGenerator, binaryWriter, module);
|
generateMethods(classes, context, generator, classGenerator, binaryWriter, module);
|
||||||
exceptionHandlingIntrinsic.postProcess(shadowStackTransformer.getCallSites());
|
exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames()));
|
||||||
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
|
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
|
||||||
classGenerator.postProcess();
|
classGenerator.postProcess();
|
||||||
mutatorIntrinsic.setStaticGcRootsAddress(classGenerator.getStaticGcRootsAddress());
|
mutatorIntrinsic.setStaticGcRootsAddress(classGenerator.getStaticGcRootsAddress());
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class CallSiteBinaryGenerator {
|
||||||
this.stringPool = stringPool;
|
this.stringPool = stringPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int writeCallSites(List<CallSiteDescriptor> callSites) {
|
public int writeCallSites(List<? extends CallSiteDescriptor> callSites) {
|
||||||
if (callSites.isEmpty()) {
|
if (callSites.isEmpty()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postProcess(List<CallSiteDescriptor> callSites) {
|
public void postProcess(List<? extends CallSiteDescriptor> callSites) {
|
||||||
int address = callSiteBinaryGenerator.writeCallSites(callSites);
|
int address = callSiteBinaryGenerator.writeCallSites(callSites);
|
||||||
for (WasmInt32Constant constant : constants) {
|
for (WasmInt32Constant constant : constants) {
|
||||||
constant.setValue(address);
|
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.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.model.AccessLevel;
|
import org.teavm.model.AccessLevel;
|
||||||
import org.teavm.model.AnnotationContainerReader;
|
import org.teavm.model.AnnotationContainerReader;
|
||||||
import org.teavm.model.AnnotationReader;
|
|
||||||
import org.teavm.model.AnnotationValue;
|
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldReader;
|
import org.teavm.model.FieldReader;
|
||||||
|
@ -48,12 +43,14 @@ public class ClassIO {
|
||||||
private ReferenceCache referenceCache;
|
private ReferenceCache referenceCache;
|
||||||
private SymbolTable symbolTable;
|
private SymbolTable symbolTable;
|
||||||
private ProgramIO programIO;
|
private ProgramIO programIO;
|
||||||
|
private AnnotationIO annotationIO;
|
||||||
|
|
||||||
public ClassIO(ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable,
|
public ClassIO(ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable,
|
||||||
SymbolTable varTable) {
|
SymbolTable varTable) {
|
||||||
this.referenceCache = referenceCache;
|
this.referenceCache = referenceCache;
|
||||||
this.symbolTable = symbolTable;
|
this.symbolTable = symbolTable;
|
||||||
programIO = new ProgramIO(referenceCache, symbolTable, fileTable, varTable);
|
programIO = new ProgramIO(referenceCache, symbolTable, fileTable, varTable);
|
||||||
|
annotationIO = new AnnotationIO(referenceCache, symbolTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeClass(OutputStream stream, ClassReader cls) throws IOException {
|
public void writeClass(OutputStream stream, ClassReader cls) throws IOException {
|
||||||
|
@ -66,7 +63,7 @@ public class ClassIO {
|
||||||
for (String iface : cls.getInterfaces()) {
|
for (String iface : cls.getInterfaces()) {
|
||||||
output.writeUnsigned(symbolTable.lookup(iface));
|
output.writeUnsigned(symbolTable.lookup(iface));
|
||||||
}
|
}
|
||||||
writeAnnotations(output, cls.getAnnotations());
|
annotationIO.writeAnnotations(output, cls.getAnnotations());
|
||||||
output.writeUnsigned(cls.getFields().size());
|
output.writeUnsigned(cls.getFields().size());
|
||||||
for (FieldReader field : cls.getFields()) {
|
for (FieldReader field : cls.getFields()) {
|
||||||
writeField(output, field);
|
writeField(output, field);
|
||||||
|
@ -93,7 +90,7 @@ public class ClassIO {
|
||||||
interfaces.add(referenceCache.getCached(symbolTable.at(input.readUnsigned())));
|
interfaces.add(referenceCache.getCached(symbolTable.at(input.readUnsigned())));
|
||||||
}
|
}
|
||||||
cls.interfaces = Collections.unmodifiableSet(interfaces);
|
cls.interfaces = Collections.unmodifiableSet(interfaces);
|
||||||
cls.annotations = readAnnotations(input);
|
cls.annotations = annotationIO.readAnnotations(input);
|
||||||
|
|
||||||
Map<String, CachedField> fields = new LinkedHashMap<>();
|
Map<String, CachedField> fields = new LinkedHashMap<>();
|
||||||
int fieldCount = input.readUnsigned();
|
int fieldCount = input.readUnsigned();
|
||||||
|
@ -120,7 +117,7 @@ public class ClassIO {
|
||||||
output.writeUnsigned(field.getLevel().ordinal());
|
output.writeUnsigned(field.getLevel().ordinal());
|
||||||
output.writeUnsigned(packModifiers(field.readModifiers()));
|
output.writeUnsigned(packModifiers(field.readModifiers()));
|
||||||
writeFieldValue(output, field.getInitialValue());
|
writeFieldValue(output, field.getInitialValue());
|
||||||
writeAnnotations(output, field.getAnnotations());
|
annotationIO.writeAnnotations(output, field.getAnnotations());
|
||||||
}
|
}
|
||||||
|
|
||||||
private CachedField readField(String className, VarDataInput input) throws IOException {
|
private CachedField readField(String className, VarDataInput input) throws IOException {
|
||||||
|
@ -130,7 +127,7 @@ public class ClassIO {
|
||||||
field.level = accessLevels[input.readUnsigned()];
|
field.level = accessLevels[input.readUnsigned()];
|
||||||
field.modifiers = unpackModifiers(input.readUnsigned());
|
field.modifiers = unpackModifiers(input.readUnsigned());
|
||||||
field.initialValue = readFieldValue(input);
|
field.initialValue = readFieldValue(input);
|
||||||
field.annotations = readAnnotations(input);
|
field.annotations = annotationIO.readAnnotations(input);
|
||||||
field.ownerName = className;
|
field.ownerName = className;
|
||||||
field.reference = referenceCache.getCached(new FieldReference(className, field.name));
|
field.reference = referenceCache.getCached(new FieldReference(className, field.name));
|
||||||
return field;
|
return field;
|
||||||
|
@ -181,15 +178,15 @@ public class ClassIO {
|
||||||
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
|
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
|
||||||
output.writeUnsigned(method.getLevel().ordinal());
|
output.writeUnsigned(method.getLevel().ordinal());
|
||||||
output.writeUnsigned(packModifiers(method.readModifiers()));
|
output.writeUnsigned(packModifiers(method.readModifiers()));
|
||||||
writeAnnotations(output, method.getAnnotations());
|
annotationIO.writeAnnotations(output, method.getAnnotations());
|
||||||
|
|
||||||
for (AnnotationContainerReader parameterAnnotation : method.getParameterAnnotations()) {
|
for (AnnotationContainerReader parameterAnnotation : method.getParameterAnnotations()) {
|
||||||
writeAnnotations(output, parameterAnnotation);
|
annotationIO.writeAnnotations(output, parameterAnnotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method.getAnnotationDefault() != null) {
|
if (method.getAnnotationDefault() != null) {
|
||||||
output.writeUnsigned(1);
|
output.writeUnsigned(1);
|
||||||
writeAnnotationValue(output, method.getAnnotationDefault());
|
annotationIO.writeAnnotationValue(output, method.getAnnotationDefault());
|
||||||
} else {
|
} else {
|
||||||
output.writeUnsigned(0);
|
output.writeUnsigned(0);
|
||||||
}
|
}
|
||||||
|
@ -212,17 +209,17 @@ public class ClassIO {
|
||||||
method.reference = referenceCache.getCached(className, descriptor);
|
method.reference = referenceCache.getCached(className, descriptor);
|
||||||
method.level = accessLevels[input.readUnsigned()];
|
method.level = accessLevels[input.readUnsigned()];
|
||||||
method.modifiers = unpackModifiers(input.readUnsigned());
|
method.modifiers = unpackModifiers(input.readUnsigned());
|
||||||
method.annotations = readAnnotations(input);
|
method.annotations = annotationIO.readAnnotations(input);
|
||||||
method.ownerName = className;
|
method.ownerName = className;
|
||||||
method.name = descriptor.getName();
|
method.name = descriptor.getName();
|
||||||
|
|
||||||
method.parameterAnnotations = new CachedAnnotations[descriptor.parameterCount()];
|
method.parameterAnnotations = new CachedAnnotations[descriptor.parameterCount()];
|
||||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||||
method.parameterAnnotations[i] = readAnnotations(input);
|
method.parameterAnnotations[i] = annotationIO.readAnnotations(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.readUnsigned() != 0) {
|
if (input.readUnsigned() != 0) {
|
||||||
method.annotationDefault = readAnnotationValue(input);
|
method.annotationDefault = annotationIO.readAnnotationValue(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.readUnsigned() != 0) {
|
if (input.readUnsigned() != 0) {
|
||||||
|
@ -239,143 +236,6 @@ public class ClassIO {
|
||||||
return method;
|
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) {
|
private int packModifiers(Set<ElementModifier> modifiers) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
|
@ -80,7 +80,7 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt
|
||||||
|
|
||||||
private Entry getEntry(String name) {
|
private Entry getEntry(String name) {
|
||||||
return cache.computeIfAbsent(name, className -> {
|
return cache.computeIfAbsent(name, className -> {
|
||||||
ClassReader cls = provider.apply(className);
|
ClassReader cls = provider != null ? provider.apply(className) : null;
|
||||||
Entry en = new Entry();
|
Entry en = new Entry();
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
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.SwitchTableEntry;
|
||||||
import org.teavm.model.instructions.SwitchTableEntryReader;
|
import org.teavm.model.instructions.SwitchTableEntryReader;
|
||||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
import org.teavm.model.util.ModelUtils;
|
||||||
|
|
||||||
public class ProgramIO {
|
public class ProgramIO {
|
||||||
private SymbolTable symbolTable;
|
private SymbolTable symbolTable;
|
||||||
private SymbolTable fileTable;
|
private SymbolTable fileTable;
|
||||||
private SymbolTable variableTable;
|
private SymbolTable variableTable;
|
||||||
private ReferenceCache referenceCache;
|
private ReferenceCache referenceCache;
|
||||||
|
private AnnotationIO annotationIO;
|
||||||
private static BinaryOperation[] binaryOperations = BinaryOperation.values();
|
private static BinaryOperation[] binaryOperations = BinaryOperation.values();
|
||||||
private static NumericOperandType[] numericOperandTypes = NumericOperandType.values();
|
private static NumericOperandType[] numericOperandTypes = NumericOperandType.values();
|
||||||
private static IntegerSubtype[] integerSubtypes = IntegerSubtype.values();
|
private static IntegerSubtype[] integerSubtypes = IntegerSubtype.values();
|
||||||
|
@ -108,6 +110,7 @@ public class ProgramIO {
|
||||||
this.symbolTable = symbolTable;
|
this.symbolTable = symbolTable;
|
||||||
this.fileTable = fileTable;
|
this.fileTable = fileTable;
|
||||||
this.variableTable = variableTable;
|
this.variableTable = variableTable;
|
||||||
|
annotationIO = new AnnotationIO(referenceCache, symbolTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(ProgramReader program, OutputStream output) throws IOException {
|
public void write(ProgramReader program, OutputStream output) throws IOException {
|
||||||
|
@ -149,6 +152,7 @@ public class ProgramIO {
|
||||||
}
|
}
|
||||||
data.writeUnsigned(0);
|
data.writeUnsigned(0);
|
||||||
}
|
}
|
||||||
|
annotationIO.writeAnnotations(data, program.getAnnotations());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program read(InputStream input) throws IOException {
|
public Program read(InputStream input) throws IOException {
|
||||||
|
@ -230,6 +234,7 @@ public class ProgramIO {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ModelUtils.copyAnnotations(annotationIO.readAnnotations(data), program.getAnnotations());
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.common;
|
package org.teavm.common;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.IntArrayList;
|
||||||
import com.carrotsearch.hppc.IntHashSet;
|
import com.carrotsearch.hppc.IntHashSet;
|
||||||
import com.carrotsearch.hppc.IntSet;
|
import com.carrotsearch.hppc.IntSet;
|
||||||
import com.carrotsearch.hppc.cursors.IntCursor;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -157,7 +157,8 @@ public class GraphIndexer {
|
||||||
List<WeightedNode> succList = new ArrayList<>(successors.length);
|
List<WeightedNode> succList = new ArrayList<>(successors.length);
|
||||||
IntegerArray orderedSuccessors = new IntegerArray(successors.length);
|
IntegerArray orderedSuccessors = new IntegerArray(successors.length);
|
||||||
if (terminalNodes.size() > 0) {
|
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) {
|
for (int succ : successors) {
|
||||||
if (loopNodes.contains(succ)) {
|
if (loopNodes.contains(succ)) {
|
||||||
succList.add(new WeightedNode(succ, priorities[succ], weights[succ]));
|
succList.add(new WeightedNode(succ, priorities[succ], weights[succ]));
|
||||||
|
@ -170,8 +171,8 @@ public class GraphIndexer {
|
||||||
|
|
||||||
IntSet outerSuccessors = new IntHashSet(successors.length);
|
IntSet outerSuccessors = new IntHashSet(successors.length);
|
||||||
succList.clear();
|
succList.clear();
|
||||||
for (IntCursor loopNode : loopNodes) {
|
for (int loopNode : loopNodeList) {
|
||||||
for (int succ : graph.outgoingEdges(loopNode.value)) {
|
for (int succ : graph.outgoingEdges(loopNode)) {
|
||||||
if (!loopNodes.contains(succ)) {
|
if (!loopNodes.contains(succ)) {
|
||||||
if (outerSuccessors.add(succ)) {
|
if (outerSuccessors.add(succ)) {
|
||||||
succList.add(new WeightedNode(succ, priorities[succ], weights[succ]));
|
succList.add(new WeightedNode(succ, priorities[succ], weights[succ]));
|
||||||
|
@ -206,7 +207,9 @@ public class GraphIndexer {
|
||||||
|
|
||||||
private int[] findNaturalLoop(int head, int[] terminals) {
|
private int[] findNaturalLoop(int head, int[] terminals) {
|
||||||
IntSet loop = new IntHashSet();
|
IntSet loop = new IntHashSet();
|
||||||
|
IntArrayList loopList = new IntArrayList();
|
||||||
loop.add(head);
|
loop.add(head);
|
||||||
|
loopList.add(head);
|
||||||
IntegerStack stack = new IntegerStack(1);
|
IntegerStack stack = new IntegerStack(1);
|
||||||
for (int pred : terminals) {
|
for (int pred : terminals) {
|
||||||
stack.push(pred);
|
stack.push(pred);
|
||||||
|
@ -216,11 +219,12 @@ public class GraphIndexer {
|
||||||
if (!loop.add(node)) {
|
if (!loop.add(node)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
loopList.add(node);
|
||||||
for (int pred : graph.incomingEdges(node)) {
|
for (int pred : graph.incomingEdges(node)) {
|
||||||
stack.push(pred);
|
stack.push(pred);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loop.toArray();
|
return loopList.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int nodeAt(int index) {
|
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;
|
unprocessedClassSource = null;
|
||||||
classSource.innerHierarchy = 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) {
|
if (classSource != agentClassSource) {
|
||||||
classHierarchy = new ClassHierarchy(agentClassSource);
|
classHierarchy = new ClassHierarchy(agentClassSource);
|
||||||
generatedClassNames.addAll(classSource.getGeneratedClassNames());
|
generatedClassNames.addAll(classSource.getGeneratedClassNames());
|
||||||
|
|
|
@ -23,6 +23,7 @@ public class Program implements ProgramReader {
|
||||||
private List<Variable> variables = new ArrayList<>();
|
private List<Variable> variables = new ArrayList<>();
|
||||||
private boolean packed;
|
private boolean packed;
|
||||||
private int lastUsedRegister;
|
private int lastUsedRegister;
|
||||||
|
private AnnotationContainer annotations = new AnnotationContainer();
|
||||||
|
|
||||||
public BasicBlock createBasicBlock() {
|
public BasicBlock createBasicBlock() {
|
||||||
BasicBlock block = new BasicBlock(this, basicBlocks.size());
|
BasicBlock block = new BasicBlock(this, basicBlocks.size());
|
||||||
|
@ -149,4 +150,9 @@ public class Program implements ProgramReader {
|
||||||
}
|
}
|
||||||
return variables.get(index);
|
return variables.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationContainer getAnnotations() {
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,4 +25,6 @@ public interface ProgramReader {
|
||||||
int variableCount();
|
int variableCount();
|
||||||
|
|
||||||
VariableReader variableAt(int index);
|
VariableReader variableAt(int index);
|
||||||
|
|
||||||
|
AnnotationContainerReader getAnnotations();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,19 @@
|
||||||
package org.teavm.model.lowlevel;
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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 {
|
public class CallSiteDescriptor {
|
||||||
private int id;
|
private int id;
|
||||||
|
@ -39,4 +51,86 @@ public class CallSiteDescriptor {
|
||||||
public List<ExceptionHandlerDescriptor> getHandlers() {
|
public List<ExceptionHandlerDescriptor> getHandlers() {
|
||||||
return handlers;
|
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;
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import org.teavm.model.AnnotationHolder;
|
||||||
|
import org.teavm.model.AnnotationReader;
|
||||||
|
import org.teavm.model.AnnotationValue;
|
||||||
|
|
||||||
public class CallSiteLocation {
|
public class CallSiteLocation {
|
||||||
private String fileName;
|
private String fileName;
|
||||||
|
@ -63,4 +66,21 @@ public class CallSiteLocation {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(fileName, className, methodName, lineNumber);
|
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;
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
|
import org.teavm.model.AnnotationHolder;
|
||||||
|
import org.teavm.model.AnnotationReader;
|
||||||
|
import org.teavm.model.AnnotationValue;
|
||||||
|
|
||||||
public class ExceptionHandlerDescriptor {
|
public class ExceptionHandlerDescriptor {
|
||||||
private int id;
|
private int id;
|
||||||
private String className;
|
private String className;
|
||||||
|
@ -31,4 +35,17 @@ public class ExceptionHandlerDescriptor {
|
||||||
public String getClassName() {
|
public String getClassName() {
|
||||||
return className;
|
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;
|
import org.teavm.runtime.ShadowStack;
|
||||||
|
|
||||||
public class ShadowStackTransformer {
|
public class ShadowStackTransformer {
|
||||||
private Characteristics managedMethodRepository;
|
private Characteristics characteristics;
|
||||||
private GCShadowStackContributor gcContributor;
|
private GCShadowStackContributor gcContributor;
|
||||||
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||||
|
|
||||||
public ShadowStackTransformer(Characteristics managedMethodRepository) {
|
public ShadowStackTransformer(Characteristics characteristics) {
|
||||||
gcContributor = new GCShadowStackContributor(managedMethodRepository);
|
gcContributor = new GCShadowStackContributor(characteristics);
|
||||||
this.managedMethodRepository = managedMethodRepository;
|
this.characteristics = characteristics;
|
||||||
}
|
|
||||||
|
|
||||||
public List<CallSiteDescriptor> getCallSites() {
|
|
||||||
return callSites;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply(Program program, MethodReader method) {
|
public void apply(Program program, MethodReader method) {
|
||||||
if (!managedMethodRepository.isManaged(method.getReference())) {
|
if (!characteristics.isManaged(method.getReference())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int shadowStackSize = gcContributor.contribute(program, method);
|
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();
|
method.getReference(), program).contribute();
|
||||||
|
List<CallSiteDescriptor> programCallSites = callSites.subList(callSiteStartIndex, callSites.size());
|
||||||
|
CallSiteDescriptor.save(programCallSites, program.getAnnotations());
|
||||||
|
|
||||||
if (shadowStackSize > 0 || exceptions) {
|
if (shadowStackSize > 0 || exceptions) {
|
||||||
addStackAllocation(program, shadowStackSize);
|
addStackAllocation(program, shadowStackSize);
|
||||||
|
|
|
@ -91,6 +91,7 @@ public final class ProgramUtils {
|
||||||
BasicBlock blockCopy = copy.basicBlockAt(i);
|
BasicBlock blockCopy = copy.basicBlockAt(i);
|
||||||
copyBasicBlock(block, blockCopy);
|
copyBasicBlock(block, blockCopy);
|
||||||
}
|
}
|
||||||
|
ModelUtils.copyAnnotations(program.getAnnotations(), copy.getAnnotations());
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -342,6 +342,7 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
|
||||||
public void visit(GetFieldInstruction insn) {
|
public void visit(GetFieldInstruction insn) {
|
||||||
String className = classNameMapper.apply(insn.getField().getClassName());
|
String className = classNameMapper.apply(insn.getField().getClassName());
|
||||||
insn.setField(referenceCache.getCached(new FieldReference(className, insn.getField().getFieldName())));
|
insn.setField(referenceCache.getCached(new FieldReference(className, insn.getField().getFieldName())));
|
||||||
|
insn.setFieldType(rename(insn.getFieldType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -350,6 +351,7 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
|
||||||
if (className != insn.getField().getClassName()) {
|
if (className != insn.getField().getClassName()) {
|
||||||
insn.setField(referenceCache.getCached(new FieldReference(className, insn.getField().getFieldName())));
|
insn.setField(referenceCache.getCached(new FieldReference(className, insn.getField().getFieldName())));
|
||||||
}
|
}
|
||||||
|
insn.setFieldType(rename(insn.getFieldType()));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void visit(InvokeInstruction insn) {
|
public void visit(InvokeInstruction insn) {
|
||||||
|
|
|
@ -26,13 +26,13 @@ public final class ExceptionHandling {
|
||||||
private ExceptionHandling() {
|
private ExceptionHandling() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static native CallSite findCallSiteById(int id);
|
public static native CallSite findCallSiteById(int id, Address frame);
|
||||||
|
|
||||||
public static void printStack() {
|
public static void printStack() {
|
||||||
Address stackFrame = ShadowStack.getStackTop();
|
Address stackFrame = ShadowStack.getStackTop();
|
||||||
while (stackFrame != null) {
|
while (stackFrame != null) {
|
||||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||||
CallSite callSite = findCallSiteById(callSiteId);
|
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||||
CallSiteLocation location = callSite.location;
|
CallSiteLocation location = callSite.location;
|
||||||
|
|
||||||
Console.printString(" at ");
|
Console.printString(" at ");
|
||||||
|
@ -74,7 +74,7 @@ public final class ExceptionHandling {
|
||||||
Address stackFrame = ShadowStack.getStackTop();
|
Address stackFrame = ShadowStack.getStackTop();
|
||||||
stackLoop: while (stackFrame != null) {
|
stackLoop: while (stackFrame != null) {
|
||||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||||
CallSite callSite = findCallSiteById(callSiteId);
|
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||||
ExceptionHandler handler = callSite.firstHandler;
|
ExceptionHandler handler = callSite.firstHandler;
|
||||||
|
|
||||||
for (int i = 0; i < callSite.handlerCount; ++i) {
|
for (int i = 0; i < callSite.handlerCount; ++i) {
|
||||||
|
@ -118,7 +118,7 @@ public final class ExceptionHandling {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (stackFrame != null && index < target.length) {
|
while (stackFrame != null && index < target.length) {
|
||||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||||
CallSite callSite = findCallSiteById(callSiteId);
|
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||||
CallSiteLocation location = callSite.location;
|
CallSiteLocation location = callSite.location;
|
||||||
StackTraceElement element = createElement(location != null ? location.className : "",
|
StackTraceElement element = createElement(location != null ? location.className : "",
|
||||||
location != null ? location.methodName : "", location != null ? location.fileName : null,
|
location != null ? location.methodName : "", location != null ? location.fileName : null,
|
||||||
|
|
|
@ -45,6 +45,8 @@ public class RuntimeClass extends RuntimeObject {
|
||||||
public IsSupertypeFunction isSupertypeOf;
|
public IsSupertypeFunction isSupertypeOf;
|
||||||
public InitFunction init;
|
public InitFunction init;
|
||||||
public RuntimeClass parent;
|
public RuntimeClass parent;
|
||||||
|
public int superinterfaceCount;
|
||||||
|
public RuntimeClassPointer superinterfaces;
|
||||||
public Address enumValues;
|
public Address enumValues;
|
||||||
public Address layout;
|
public Address layout;
|
||||||
public RuntimeObject simpleName;
|
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;
|
package org.teavm.vm;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
@ -41,7 +40,7 @@ public class MemoryBuildTarget implements BuildTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OutputStream createResource(String fileName) throws IOException {
|
public OutputStream createResource(String fileName) {
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
data.put(fileName, stream);
|
data.put(fileName, stream);
|
||||||
return stream;
|
return stream;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <uchar.h>
|
#include <uchar.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -36,6 +35,8 @@ typedef struct TeaVM_Class {
|
||||||
int32_t (*isSupertypeOf)(struct TeaVM_Class*);
|
int32_t (*isSupertypeOf)(struct TeaVM_Class*);
|
||||||
void (*init)();
|
void (*init)();
|
||||||
struct TeaVM_Class* superclass;
|
struct TeaVM_Class* superclass;
|
||||||
|
int32_t superinterfaceCount;
|
||||||
|
struct TeaVM_Class** superinterfaces;
|
||||||
void* enumValues;
|
void* enumValues;
|
||||||
void* layout;
|
void* layout;
|
||||||
TeaVM_Object* simpleName;
|
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();
|
return obj == NULL || cls(TEAVM_CLASS_OF(obj)) ? obj : teavm_throwClassCastException();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TEAVM_ALLOC_STACK(size) \
|
#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]; \
|
void* teavm__shadowStack__[(size) + 3]; \
|
||||||
teavm__shadowStack__[0] = teavm_stackTop; \
|
teavm__shadowStack__[0] = teavm_stackTop; \
|
||||||
teavm__shadowStack__[2] = (void*) size; \
|
teavm__shadowStack__[2] = (void*) size; \
|
||||||
teavm_stackTop = teavm__shadowStack__
|
teavm_stackTop = teavm__shadowStack__
|
||||||
|
|
||||||
|
#define TEAVM_STACK_HEADER_ADD_SIZE 0
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define TEAVM_RELEASE_STACK teavm_stackTop = teavm__shadowStack__[0]
|
#define TEAVM_RELEASE_STACK teavm_stackTop = teavm__shadowStack__[0]
|
||||||
#define TEAVM_GC_ROOT(index, ptr) teavm__shadowStack__[3 + (index)] = ptr
|
#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 + (index)] = NULL
|
#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_CALL_SITE(id) (teavm__shadowStack__[1] = (void*) (id))
|
||||||
#define TEAVM_EXCEPTION_HANDLER ((int32_t) (intptr_t) (teavm__shadowStack__[1]))
|
#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))
|
#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_ADDRESS_ADD(address, offset) ((char *) (address) + (offset))
|
||||||
#define TEAVM_STRUCTURE_ADD(structure, address, offset) (((structure*) (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) { \
|
#define TEAVM_STRING(length, hash, s) { \
|
||||||
.characters = (TeaVM_Array*) & (struct { TeaVM_Array hdr; char16_t data[(length) + 1]; }) { \
|
.characters = (TeaVM_Array*) & (struct { TeaVM_Array hdr; char16_t data[(length) + 1]; }) { \
|
||||||
.hdr = { .size = length }, \
|
.hdr = { .size = length }, \
|
||||||
|
|
1
pom.xml
1
pom.xml
|
@ -89,6 +89,7 @@
|
||||||
<module>jso/impl</module>
|
<module>jso/impl</module>
|
||||||
<module>html4j</module>
|
<module>html4j</module>
|
||||||
<module>platform</module>
|
<module>platform</module>
|
||||||
|
<module>tools/c-incremental</module>
|
||||||
<module>tools/core</module>
|
<module>tools/core</module>
|
||||||
<module>tools/maven</module>
|
<module>tools/maven</module>
|
||||||
<module>tools/chrome-rdp</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>
|
<artifactId>teavm-devserver</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.teavm</groupId>
|
||||||
|
<artifactId>teavm-c-incremental</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.teavm</groupId>
|
<groupId>org.teavm</groupId>
|
||||||
<artifactId>teavm-jso-impl</artifactId>
|
<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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.devserver;
|
package org.teavm.tooling.builder;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.teavm.callgraph.CallGraph;
|
import org.teavm.callgraph.CallGraph;
|
||||||
import org.teavm.diagnostics.ProblemProvider;
|
import org.teavm.diagnostics.ProblemProvider;
|
||||||
import org.teavm.tooling.InstructionLocationReader;
|
import org.teavm.tooling.InstructionLocationReader;
|
||||||
import org.teavm.tooling.builder.BuildResult;
|
|
||||||
import org.teavm.vm.TeaVM;
|
import org.teavm.vm.TeaVM;
|
||||||
|
|
||||||
class CodeServletBuildResult implements BuildResult {
|
public class SimpleBuildResult implements BuildResult {
|
||||||
private TeaVM vm;
|
private TeaVM vm;
|
||||||
private List<String> generatedFiles;
|
private List<String> generatedFiles;
|
||||||
private Collection<String> usedResources;
|
private Collection<String> usedResources;
|
||||||
|
|
||||||
public CodeServletBuildResult(TeaVM vm, List<String> generatedFiles) {
|
public SimpleBuildResult(TeaVM vm, List<String> generatedFiles) {
|
||||||
this.vm = vm;
|
this.vm = vm;
|
||||||
this.generatedFiles = generatedFiles;
|
this.generatedFiles = generatedFiles;
|
||||||
}
|
}
|
|
@ -86,6 +86,7 @@ import org.teavm.parsing.resource.ResourceClassHolderMapper;
|
||||||
import org.teavm.tooling.EmptyTeaVMToolLog;
|
import org.teavm.tooling.EmptyTeaVMToolLog;
|
||||||
import org.teavm.tooling.TeaVMProblemRenderer;
|
import org.teavm.tooling.TeaVMProblemRenderer;
|
||||||
import org.teavm.tooling.TeaVMToolLog;
|
import org.teavm.tooling.TeaVMToolLog;
|
||||||
|
import org.teavm.tooling.builder.SimpleBuildResult;
|
||||||
import org.teavm.tooling.util.FileSystemWatcher;
|
import org.teavm.tooling.util.FileSystemWatcher;
|
||||||
import org.teavm.vm.MemoryBuildTarget;
|
import org.teavm.vm.MemoryBuildTarget;
|
||||||
import org.teavm.vm.TeaVM;
|
import org.teavm.vm.TeaVM;
|
||||||
|
@ -995,7 +996,7 @@ public class CodeServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireBuildComplete(TeaVM vm) {
|
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) {
|
for (DevServerListener listener : listeners) {
|
||||||
listener.compilationComplete(result);
|
listener.compilationComplete(result);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user