C: incremental code generator

This commit is contained in:
Alexey Andreev 2019-05-14 14:01:39 +03:00
parent c1d4ed2e3c
commit 054db3e8d1
50 changed files with 1890 additions and 275 deletions

View File

@ -18,14 +18,12 @@ package org.teavm.backend.c;
import com.carrotsearch.hppc.ObjectByteHashMap;
import com.carrotsearch.hppc.ObjectByteMap;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
@ -48,6 +46,7 @@ import org.teavm.backend.c.generate.IncludeManager;
import org.teavm.backend.c.generate.NameProvider;
import org.teavm.backend.c.generate.OutputFileUtil;
import org.teavm.backend.c.generate.SimpleIncludeManager;
import org.teavm.backend.c.generate.SimpleStringPool;
import org.teavm.backend.c.generate.StringPool;
import org.teavm.backend.c.generate.StringPoolGenerator;
import org.teavm.backend.c.generators.ArrayGenerator;
@ -102,6 +101,7 @@ import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.lowlevel.CallSiteDescriptor;
import org.teavm.model.lowlevel.Characteristics;
import org.teavm.model.lowlevel.ClassInitializerEliminator;
import org.teavm.model.lowlevel.ClassInitializerTransformer;
@ -141,12 +141,27 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private int minHeapSize = 32 * 1024 * 1024;
private List<IntrinsicFactory> intrinsicFactories = new ArrayList<>();
private List<GeneratorFactory> generatorFactories = new ArrayList<>();
private Characteristics characteristics;
private Set<MethodReference> asyncMethods;
private boolean incremental;
private StringPool stringPool;
public CTarget() {
this(new SimpleStringPool());
}
public CTarget(StringPool stringPool) {
this.stringPool = stringPool;
}
public void setMinHeapSize(int minHeapSize) {
this.minHeapSize = minHeapSize;
}
public void setIncremental(boolean incremental) {
this.incremental = incremental;
}
@Override
public List<ClassHolderTransformer> getTransformers() {
List<ClassHolderTransformer> transformers = new ArrayList<>();
@ -163,7 +178,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
@Override
public void setController(TeaVMTargetController controller) {
this.controller = controller;
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
characteristics = new Characteristics(controller.getUnprocessedClassSource());
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
classInitializerTransformer = new ClassInitializerTransformer();
shadowStackTransformer = new ShadowStackTransformer(characteristics);
@ -273,6 +288,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
nullCheckTransformation.apply(program, method.getResultType());
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods)
.apply(program, method.getReference());
ShadowStackTransformer shadowStackTransformer = !incremental
? this.shadowStackTransformer
: new ShadowStackTransformer(characteristics);
shadowStackTransformer.apply(program, method);
}
@ -281,7 +299,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
ClassHierarchy hierarchy = new ClassHierarchy(classes);
TagRegistry tagRegistry = new TagRegistry(classes, hierarchy);
StringPool stringPool = new StringPool();
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false, true);
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
@ -313,12 +330,18 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
intrinsics, generators, asyncMethods::contains, buildTarget);
intrinsics, generators, asyncMethods::contains, buildTarget, incremental);
BufferedCodeWriter runtimeWriter = new BufferedCodeWriter();
copyResource("runtime.h", "runtime.h", buildTarget);
BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter();
emitResource(runtimeWriter, "runtime.c");
runtimeHeaderWriter.println("#pragma once");
if (incremental) {
runtimeHeaderWriter.println("#define TEAVM_INCREMENTAL true");
}
emitResource(runtimeHeaderWriter, "runtime.h");
ClassGenerator classGenerator = new ClassGenerator(context, controller.getUnprocessedClassSource(),
tagRegistry, decompiler);
IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl(
@ -334,8 +357,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
generateClasses(classes, classGenerator, buildTarget);
generateSpecialFunctions(context, runtimeWriter);
OutputFileUtil.write(runtimeWriter, "runtime.c", buildTarget);
OutputFileUtil.write(runtimeHeaderWriter, "runtime.h", buildTarget);
generateCallSites(buildTarget, context);
generateCallSites(buildTarget, context, classes.getClassNames());
generateStrings(buildTarget, stringPool);
List<ValueType> types = classGenerator.getTypes().stream()
@ -362,25 +386,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
}
}
private void copyResource(String resourceName, String targetName, BuildTarget buildTarget) {
ClassLoader classLoader = CTarget.class.getClassLoader();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
classLoader.getResourceAsStream("org/teavm/backend/c/" + resourceName)));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
buildTarget.createResource(targetName), StandardCharsets.UTF_8))) {
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
writer.write(line);
writer.write('\n');
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void generateClasses(ListableClassHolderSource classes, ClassGenerator classGenerator,
BuildTarget buildTarget) throws IOException {
List<String> classNames = sortClassNames(classes);
@ -444,7 +449,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
return classNames;
}
private void generateCallSites(BuildTarget buildTarget, GenerationContext context) throws IOException {
private void generateCallSites(BuildTarget buildTarget, GenerationContext context,
Collection<? extends String> classNames) throws IOException {
BufferedCodeWriter writer = new BufferedCodeWriter();
BufferedCodeWriter headerWriter = new BufferedCodeWriter();
@ -458,15 +464,32 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
headerIncludes.includePath("runtime.h");
headerIncludes.includeClass(CallSiteGenerator.CALL_SITE);
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
headerWriter.println("extern " + callSiteName + " teavm_callSites[];");
new CallSiteGenerator(context, writer, includes).generate(shadowStackTransformer.getCallSites());
if (incremental) {
generateIncrementalCallSites(context, headerWriter);
} else {
generateFastCallSites(context, writer, includes, headerWriter, classNames);
}
OutputFileUtil.write(writer, "callsites.c", buildTarget);
OutputFileUtil.write(headerWriter, "callsites.h", buildTarget);
}
private void generateFastCallSites(GenerationContext context, CodeWriter writer, IncludeManager includes,
CodeWriter headerWriter, Collection<? extends String> classNames) {
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
headerWriter.println("extern " + callSiteName + " teavm_callSites[];");
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) (teavm_callSites + id)");
new CallSiteGenerator(context, writer, includes, "teavm_callSites")
.generate(CallSiteDescriptor.extract(context.getClassSource(), classNames));
}
private void generateIncrementalCallSites(GenerationContext context, CodeWriter headerWriter) {
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) (((" + callSiteName
+ "*) ((void**) frame)[3]) + id)");
}
private void generateStrings(BuildTarget buildTarget, StringPool stringPool) throws IOException {
BufferedCodeWriter writer = new BufferedCodeWriter();
BufferedCodeWriter headerWriter = new BufferedCodeWriter();
@ -685,7 +708,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
int size = context.getStringPool().getStrings().size();
writer.println("for (int i = 0; i < " + size + "; ++i) {").indent();
writer.println("((TeaVM_Object*) (teavm_stringPool + i))->header = stringHeader;");
writer.println("TeaVM_Object *s = (TeaVM_Object*) (teavm_stringPool + i);");
writer.println("if (s != NULL) s->header = stringHeader;");
writer.outdent().println("}");
}

View File

@ -38,16 +38,24 @@ public class CallSiteGenerator {
private List<ExceptionHandlerDescriptor> exceptionHandlers = new ArrayList<>();
private String callSiteLocationName;
private String exceptionHandlerName;
private String callSitesName;
private boolean isStatic;
public CallSiteGenerator(GenerationContext context, CodeWriter writer, IncludeManager includes) {
public CallSiteGenerator(GenerationContext context, CodeWriter writer, IncludeManager includes,
String callSitesName) {
this.context = context;
this.writer = writer;
this.includes = includes;
callSiteLocationName = context.getNames().forClass(CALL_SITE_LOCATION);
exceptionHandlerName = context.getNames().forClass(EXCEPTION_HANDLER);
this.callSitesName = callSitesName;
}
public void generate(List<CallSiteDescriptor> callSites) {
public void setStatic(boolean isStatic) {
this.isStatic = isStatic;
}
public void generate(List<? extends CallSiteDescriptor> callSites) {
CodeWriter writerForLocations = writer.fragment();
generateCallSites(callSites);
@ -58,12 +66,15 @@ public class CallSiteGenerator {
writer = oldWriter;
}
private void generateCallSites(List<CallSiteDescriptor> callSites) {
private void generateCallSites(List<? extends CallSiteDescriptor> callSites) {
String callSiteName = context.getNames().forClass(CALL_SITE);
includes.includeClass(CALL_SITE);
includes.includePath("strings.h");
writer.print(callSiteName).print(" teavm_callSites[" + callSites.size() + "] = {").indent();
if (isStatic) {
writer.print("static ");
}
writer.print(callSiteName).print(" " + callSitesName + "[" + callSites.size() + "] = {").indent();
String handlerCountName = fieldName(CALL_SITE, "handlerCount");
String firstHandlerName = fieldName(CALL_SITE, "firstHandler");
String locationName = fieldName(CALL_SITE, "location");
@ -86,14 +97,14 @@ public class CallSiteGenerator {
}
String firstHandlerExpr = !callSite.getHandlers().isEmpty()
? "teavm_exceptionHandlers + " + exceptionHandlers.size()
? "exceptionHandlers_" + callSitesName + " + " + exceptionHandlers.size()
: "NULL";
writer.println().print("{ ");
writer.print(".").print(handlerCountName).print(" = ")
.print(String.valueOf(callSite.getHandlers().size())).print(", ");
writer.print(".").print(firstHandlerName).print(" = ").print(firstHandlerExpr).print(", ");
writer.print(".").print(locationName).print(" = ")
.print(locationIndex >= 0 ? "teavm_callSiteLocations + " + locationIndex : "NULL");
.print(locationIndex >= 0 ? "callSiteLocations_" + callSitesName + " + " + locationIndex : "NULL");
writer.print(" }");
exceptionHandlers.addAll(callSite.getHandlers());
@ -104,8 +115,8 @@ public class CallSiteGenerator {
private void generateLocations() {
includes.includeClass(CALL_SITE_LOCATION);
writer.print("static ").print(callSiteLocationName).print(" teavm_callSiteLocations[" + locations.size()
+ "] = {").indent();
writer.print("static ").print(callSiteLocationName).print(" callSiteLocations_" + callSitesName
+ "[" + locations.size() + "] = {").indent();
String fileNameName = fieldName(CALL_SITE_LOCATION, "fileName");
String classNameName = fieldName(CALL_SITE_LOCATION, "className");
@ -137,7 +148,7 @@ public class CallSiteGenerator {
private void generateHandlers() {
includes.includeClass(EXCEPTION_HANDLER);
writer.print("static ").print(exceptionHandlerName).print(" teavm_exceptionHandlers["
writer.print("static ").print(exceptionHandlerName).print(" exceptionHandlers_" + callSitesName + "["
+ exceptionHandlers.size() + "] = {").indent();
String idName = fieldName(EXCEPTION_HANDLER, "id");

View File

@ -18,6 +18,7 @@ package org.teavm.backend.c.generate;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
@ -57,7 +58,9 @@ import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
import org.teavm.model.instructions.InstructionVisitor;
import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.lowlevel.CallSiteDescriptor;
import org.teavm.model.lowlevel.Characteristics;
import org.teavm.runtime.CallSite;
import org.teavm.runtime.RuntimeArray;
import org.teavm.runtime.RuntimeClass;
import org.teavm.runtime.RuntimeObject;
@ -81,6 +84,7 @@ public class ClassGenerator {
private CodeWriter headerWriter;
private IncludeManager includes;
private IncludeManager headerIncludes;
private boolean stackDefined;
public ClassGenerator(GenerationContext context, ClassReaderSource unprocessedClassSource,
TagRegistry tagRegistry, Decompiler decompiler) {
@ -161,7 +165,9 @@ public class ClassGenerator {
};
public void generateClass(CodeWriter writer, CodeWriter headerWriter, ClassHolder cls) {
stackDefined = false;
init(writer, headerWriter, fileName(cls.getName()));
codeGenerator = new CodeGenerator(context, codeWriter, includes);
String sysInitializerName = context.getNames().forClassSystemInitializer(cls.getName());
@ -180,6 +186,12 @@ public class ClassGenerator {
generateLayoutArray(cls.getName());
}
private void generateCallSites(List<? extends CallSiteDescriptor> callSites, String callSitesName) {
CallSiteGenerator generator = new CallSiteGenerator(context, codeWriter, includes, callSitesName);
generator.setStatic(true);
generator.generate(callSites);
}
public void generateType(CodeWriter writer, CodeWriter headerWriter, ValueType type) {
init(writer, headerWriter, fileName(type));
includes.includeType(type);
@ -220,6 +232,24 @@ public class ClassGenerator {
continue;
}
if (context.isIncremental()) {
String callSitesName;
List<? extends CallSiteDescriptor> callSites = CallSiteDescriptor.extract(method.getProgram());
if (!callSites.isEmpty()) {
callSitesName = "callsites_" + context.getNames().forMethod(method.getReference());
includes.includeClass(CallSite.class.getName());
generateCallSites(callSites, callSitesName);
} else {
callSitesName = "NULL";
}
if (stackDefined) {
codeWriter.println("#undef TEAVM_ALLOC_STACK");
}
codeWriter.println("#define TEAVM_ALLOC_STACK(size) TEAVM_ALLOC_STACK_DEF(size, "
+ callSitesName + ")");
stackDefined = true;
}
generateMethodForwardDeclaration(method);
RegularMethodNode methodNode = decompiler.decompileRegular(method);
codeGenerator.generateMethod(methodNode);
@ -454,6 +484,8 @@ public class ClassGenerator {
int flags = 0;
String layout = "NULL";
String initFunction = "NULL";
String superinterfaceCount = "0";
String superinterfaces = "NULL";
if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName();
@ -473,7 +505,7 @@ public class ClassGenerator {
flags |= RuntimeClass.ENUM;
}
List<TagRegistry.Range> ranges = tagRegistry.getRanges(className);
tag = ranges != null && !ranges.isEmpty() ? ranges.get(0).lower : 0;
tag = !context.isIncremental() && ranges != null && !ranges.isEmpty() ? ranges.get(0).lower : 0;
if (cls != null && cls.getParent() != null && types.contains(ValueType.object(cls.getParent()))) {
includes.includeClass(cls.getParent());
@ -487,10 +519,31 @@ public class ClassGenerator {
if (cls != null && needsInitializer(cls)) {
initFunction = context.getNames().forClassInitializer(className);
}
Set<String> interfaces = cls != null
? cls.getInterfaces().stream()
.filter(c -> types.contains(ValueType.object(c)))
.collect(Collectors.toSet())
: Collections.emptySet();
if (!interfaces.isEmpty()) {
superinterfaceCount = Integer.toString(cls.getInterfaces().size());
StringBuilder sb = new StringBuilder("(TeaVM_Class*[]) { ");
boolean first = true;
for (String itf : interfaces) {
if (!first) {
sb.append(", ");
}
first = false;
includes.includeClass(itf);
sb.append("(TeaVM_Class*) &").append(context.getNames().forClassInstance(ValueType.object(itf)));
}
superinterfaces = sb.append(" }").toString();
}
} else if (type instanceof ValueType.Array) {
includes.includeClass("java.lang.Object");
parent = "(TeaVM_Class*) &" + context.getNames().forClassInstance(ValueType.object("java.lang.Object"));
tag = tagRegistry.getRanges("java.lang.Object").get(0).lower;
tag = !context.isIncremental() ? tagRegistry.getRanges("java.lang.Object").get(0).lower : 0;
ValueType itemType = ((ValueType.Array) type).getItemType();
sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(itemType) + ")";
includes.includeType(itemType);
@ -532,6 +585,8 @@ public class ClassGenerator {
codeWriter.println(".itemType = " + itemTypeExpr + ",");
codeWriter.println(".isSupertypeOf = &" + superTypeFunction + ",");
codeWriter.println(".superclass = " + parent + ",");
codeWriter.println(".superinterfaceCount = " + superinterfaceCount + ",");
codeWriter.println(".superinterfaces = " + superinterfaces + ",");
codeWriter.println(".enumValues = NULL,");
codeWriter.println(".layout = " + layout + ",");
codeWriter.println(".enumValues = " + enumConstants + ",");
@ -566,6 +621,7 @@ public class ClassGenerator {
if (type instanceof ValueType.Object) {
String className = ((ValueType.Object) type).getClassName();
return !context.getCharacteristics().isStructure(className)
&& !context.getCharacteristics().isFunction(className)
&& !className.equals(Address.class.getName());
} else {
return type instanceof ValueType.Array;
@ -766,6 +822,14 @@ public class ClassGenerator {
}
private void generateIsSuperclassFunction(String className) {
if (context.isIncremental()) {
generateIncrementalSuperclassFunction(className);
} else {
generateFastIsSuperclassFunction(className);
}
}
private void generateFastIsSuperclassFunction(String className) {
List<TagRegistry.Range> ranges = tagRegistry.getRanges(className);
if (ranges.isEmpty()) {
codeWriter.println("return INT32_C(0);");
@ -789,6 +853,25 @@ public class ClassGenerator {
codeWriter.println("return INT32_C(1);");
}
private void generateIncrementalSuperclassFunction(String className) {
String functionName = context.getNames().forSupertypeFunction(ValueType.object(className));
ClassReader cls = context.getClassSource().get(className);
if (cls != null && types.contains(ValueType.object(className))) {
includes.includeClass(className);
String name = context.getNames().forClassInstance(ValueType.object(className));
codeWriter.println("if (cls == (TeaVM_Class*) &" + name + ") return INT32_C(1);");
codeWriter.println("if (cls->superclass != NULL && " + functionName + "(cls->superclass)) "
+ "return INT32_C(1);");
codeWriter.println("for (int32_t i = 0; i < cls->superinterfaceCount; ++i) {").indent();
codeWriter.println("if (" + functionName + "(cls->superinterfaces[i])) "
+ "return INT32_C(1);");
codeWriter.outdent().println("}");
}
codeWriter.println("return INT32_C(0);");
}
private void generateIsSuperArrayFunction(ValueType itemType) {
String itemTypeName = context.getNames().forMemberField(new FieldReference(
RuntimeClass.class.getName(), "itemType"));

View File

@ -517,6 +517,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
writer.print("TEAVM_ARRAY_DATA(TEAVM_FIELD(");
String typeName = ((ValueType.Object) type).getClassName();
expr.getArguments().get(i).acceptVisitor(this);
includes.includeClass(typeName);
writer.print(", ").print(names.forClass(typeName)).print(", ")
.print(names.forMemberField(new FieldReference(typeName, "array"))).print(")");
writer.print(", ").print(BUFFER_TYPES.get(typeName)).print(")");
@ -1016,6 +1017,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
ClassGenerator.escape(name, sb);
return sb.toString();
}
@Override
public boolean isIncremental() {
return context.isIncremental();
}
};
private static CVariableType typeToCType(ValueType type) {

View File

@ -43,11 +43,12 @@ public class GenerationContext {
private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>();
private Predicate<MethodReference> asyncMethods;
private BuildTarget buildTarget;
private boolean incremental;
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators,
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget) {
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget, boolean incremental) {
this.virtualTableProvider = virtualTableProvider;
this.characteristics = characteristics;
this.dependencies = dependencies;
@ -59,6 +60,7 @@ public class GenerationContext {
this.generators = new ArrayList<>(generators);
this.asyncMethods = asyncMethods;
this.buildTarget = buildTarget;
this.incremental = incremental;
}
public void addIntrinsic(Intrinsic intrinsic) {
@ -118,4 +120,8 @@ public class GenerationContext {
public BuildTarget getBuildTarget() {
return buildTarget;
}
public boolean isIncremental() {
return incremental;
}
}

View File

@ -55,7 +55,8 @@ public class NameProvider extends LowLevelNameProvider {
memberFieldNames.put(new FieldReference(String.class.getName(), "hashCode"), "hashCode");
for (String name : new String[] { "size", "flags", "tag", "canary", "name", "itemType", "arrayType",
"isSupertypeOf", "init", "enumValues", "layout", "simpleName" }) {
"isSupertypeOf", "init", "enumValues", "layout", "simpleName", "superinterfaceCount",
"superinterfaces" }) {
memberFieldNames.put(new FieldReference(RuntimeClass.class.getName(), name), name);
}
memberFieldNames.put(new FieldReference(RuntimeClass.class.getName(), "parent"), "superclass");

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Alexey Andreev.
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,28 +15,10 @@
*/
package org.teavm.backend.c.generate;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class StringPool {
private final ObjectIntMap<String> stringIndexes = new ObjectIntHashMap<>();
private final List<String> strings = new ArrayList<>();
private final List<String> readonlyStrings = Collections.unmodifiableList(strings);
public interface StringPool {
int getStringIndex(String string);
public int getStringIndex(String string) {
int index = stringIndexes.getOrDefault(string, -1);
if (index < 0) {
index = strings.size();
stringIndexes.put(string, index);
strings.add(string);
}
return index;
}
public List<String> getStrings() {
return readonlyStrings;
}
List<String> getStrings();
}

View File

@ -28,16 +28,20 @@ public class StringPoolGenerator {
writer.println("TeaVM_String teavm_stringPool[" + strings.size() + "] = {").indent();
for (int i = 0; i < strings.size(); ++i) {
String s = strings.get(i);
boolean codes = hasBadCharacters(s);
String macroName = codes ? "TEAVM_STRING_FROM_CODES" : "TEAVM_STRING";
writer.print(macroName + "(" + s.length() + ", " + s.hashCode() + ",");
if (codes) {
generateNumericStringLiteral(s);
if (s == null) {
writer.println("TEAVM_NULL_STRING");
} else {
writer.print("u");
generateSimpleStringLiteral(writer, s);
boolean codes = hasBadCharacters(s);
String macroName = codes ? "TEAVM_STRING_FROM_CODES" : "TEAVM_STRING";
writer.print(macroName + "(" + s.length() + ", " + s.hashCode() + ",");
if (codes) {
generateNumericStringLiteral(s);
} else {
writer.print("u");
generateSimpleStringLiteral(writer, s);
}
writer.print(")");
}
writer.print(")");
writer.print(i < strings.size() - 1 ? "," : " ");
writer.print(" // string #" + i);

View File

@ -39,8 +39,10 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
switch (invocation.getMethod().getName()) {
case "findCallSiteById":
context.includes().includePath("callsites.h");
context.writer().print("(teavm_callSites + ");
context.writer().print("TEAVM_FIND_CALLSITE(");
context.emit(invocation.getArguments().get(0));
context.writer().print(", ");
context.emit(invocation.getArguments().get(1));
context.writer().print(")");
break;
}

View File

@ -39,4 +39,6 @@ public interface IntrinsicContext {
IncludeManager includes();
String escapeFileName(String name);
boolean isIncremental();
}

View File

@ -78,15 +78,16 @@ public class ShadowStackIntrinsic implements Intrinsic {
context.writer().print(")[0]");
return;
case "getStackRootCount":
context.writer().print("((int32_t) (intptr_t) ((void**) ");
context.writer().print("TEAVM_GC_ROOTS_COUNT(");
context.emit(invocation.getArguments().get(0));
context.writer().print(")[2])");
context.writer().print(")");
return;
case "getStackRootPointer":
context.writer().print("&((void**) ");
case "getStackRootPointer": {
context.writer().print("TEAVM_GET_GC_ROOTS(");
context.emit(invocation.getArguments().get(0));
context.writer().print(")[3]");
context.writer().print(")");
return;
}
case "getCallSiteId":
context.writer().print("((int32_t) (intptr_t) ((void**) ");
context.emit(invocation.getArguments().get(0));

View File

@ -34,7 +34,7 @@ public class ExceptionHandlingDependencyListener extends AbstractDependencyListe
public void methodReached(DependencyAgent agent, MethodDependency method) {
if (method.getReference().equals(FILL_IN_STACK_TRACE)) {
DependencyNode node = agent.linkField(STACK_TRACE).getValue();
node.propagate(agent.getType("[java/lang.StackTraceElement;"));
node.propagate(agent.getType("[Ljava/lang/StackTraceElement;"));
node.getArrayItem().propagate(agent.getType("java.lang.StackTraceElement"));
MethodDependency initElem = agent.linkMethod(STACK_TRACE_ELEMENT_INIT);

View File

@ -127,6 +127,7 @@ import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.lowlevel.CallSiteDescriptor;
import org.teavm.model.lowlevel.Characteristics;
import org.teavm.model.lowlevel.ClassInitializerEliminator;
import org.teavm.model.lowlevel.ClassInitializerTransformer;
@ -374,7 +375,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
int pages = (minHeapSize + pageSize - 1) / pageSize;
module.setMemorySize(pages);
generateMethods(classes, context, generator, classGenerator, binaryWriter, module);
exceptionHandlingIntrinsic.postProcess(shadowStackTransformer.getCallSites());
exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames()));
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
classGenerator.postProcess();
mutatorIntrinsic.setStaticGcRootsAddress(classGenerator.getStaticGcRootsAddress());

View File

@ -62,7 +62,7 @@ public class CallSiteBinaryGenerator {
this.stringPool = stringPool;
}
public int writeCallSites(List<CallSiteDescriptor> callSites) {
public int writeCallSites(List<? extends CallSiteDescriptor> callSites) {
if (callSites.isEmpty()) {
return 0;
}

View File

@ -55,7 +55,7 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
return false;
}
public void postProcess(List<CallSiteDescriptor> callSites) {
public void postProcess(List<? extends CallSiteDescriptor> callSites) {
int address = callSiteBinaryGenerator.writeCallSites(callSites);
for (WasmInt32Constant constant : constants) {
constant.setValue(address);

View 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);
}
}
}

View File

@ -20,19 +20,14 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.model.AccessLevel;
import org.teavm.model.AnnotationContainerReader;
import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReader;
@ -48,12 +43,14 @@ public class ClassIO {
private ReferenceCache referenceCache;
private SymbolTable symbolTable;
private ProgramIO programIO;
private AnnotationIO annotationIO;
public ClassIO(ReferenceCache referenceCache, SymbolTable symbolTable, SymbolTable fileTable,
SymbolTable varTable) {
this.referenceCache = referenceCache;
this.symbolTable = symbolTable;
programIO = new ProgramIO(referenceCache, symbolTable, fileTable, varTable);
annotationIO = new AnnotationIO(referenceCache, symbolTable);
}
public void writeClass(OutputStream stream, ClassReader cls) throws IOException {
@ -66,7 +63,7 @@ public class ClassIO {
for (String iface : cls.getInterfaces()) {
output.writeUnsigned(symbolTable.lookup(iface));
}
writeAnnotations(output, cls.getAnnotations());
annotationIO.writeAnnotations(output, cls.getAnnotations());
output.writeUnsigned(cls.getFields().size());
for (FieldReader field : cls.getFields()) {
writeField(output, field);
@ -93,7 +90,7 @@ public class ClassIO {
interfaces.add(referenceCache.getCached(symbolTable.at(input.readUnsigned())));
}
cls.interfaces = Collections.unmodifiableSet(interfaces);
cls.annotations = readAnnotations(input);
cls.annotations = annotationIO.readAnnotations(input);
Map<String, CachedField> fields = new LinkedHashMap<>();
int fieldCount = input.readUnsigned();
@ -120,7 +117,7 @@ public class ClassIO {
output.writeUnsigned(field.getLevel().ordinal());
output.writeUnsigned(packModifiers(field.readModifiers()));
writeFieldValue(output, field.getInitialValue());
writeAnnotations(output, field.getAnnotations());
annotationIO.writeAnnotations(output, field.getAnnotations());
}
private CachedField readField(String className, VarDataInput input) throws IOException {
@ -130,7 +127,7 @@ public class ClassIO {
field.level = accessLevels[input.readUnsigned()];
field.modifiers = unpackModifiers(input.readUnsigned());
field.initialValue = readFieldValue(input);
field.annotations = readAnnotations(input);
field.annotations = annotationIO.readAnnotations(input);
field.ownerName = className;
field.reference = referenceCache.getCached(new FieldReference(className, field.name));
return field;
@ -181,15 +178,15 @@ public class ClassIO {
output.writeUnsigned(symbolTable.lookup(method.getDescriptor().toString()));
output.writeUnsigned(method.getLevel().ordinal());
output.writeUnsigned(packModifiers(method.readModifiers()));
writeAnnotations(output, method.getAnnotations());
annotationIO.writeAnnotations(output, method.getAnnotations());
for (AnnotationContainerReader parameterAnnotation : method.getParameterAnnotations()) {
writeAnnotations(output, parameterAnnotation);
annotationIO.writeAnnotations(output, parameterAnnotation);
}
if (method.getAnnotationDefault() != null) {
output.writeUnsigned(1);
writeAnnotationValue(output, method.getAnnotationDefault());
annotationIO.writeAnnotationValue(output, method.getAnnotationDefault());
} else {
output.writeUnsigned(0);
}
@ -212,17 +209,17 @@ public class ClassIO {
method.reference = referenceCache.getCached(className, descriptor);
method.level = accessLevels[input.readUnsigned()];
method.modifiers = unpackModifiers(input.readUnsigned());
method.annotations = readAnnotations(input);
method.annotations = annotationIO.readAnnotations(input);
method.ownerName = className;
method.name = descriptor.getName();
method.parameterAnnotations = new CachedAnnotations[descriptor.parameterCount()];
for (int i = 0; i < method.parameterCount(); ++i) {
method.parameterAnnotations[i] = readAnnotations(input);
method.parameterAnnotations[i] = annotationIO.readAnnotations(input);
}
if (input.readUnsigned() != 0) {
method.annotationDefault = readAnnotationValue(input);
method.annotationDefault = annotationIO.readAnnotationValue(input);
}
if (input.readUnsigned() != 0) {
@ -239,143 +236,6 @@ public class ClassIO {
return method;
}
private void writeAnnotations(VarDataOutput output, AnnotationContainerReader annotations) throws IOException {
List<AnnotationReader> annotationList = new ArrayList<>();
for (AnnotationReader annot : annotations.all()) {
annotationList.add(annot);
}
output.writeUnsigned(annotationList.size());
for (AnnotationReader annot : annotationList) {
writeAnnotation(output, annot);
}
}
private CachedAnnotations readAnnotations(VarDataInput input) throws IOException {
Map<String, CachedAnnotation> annotations = new HashMap<>();
int annotCount = input.readUnsigned();
for (int i = 0; i < annotCount; ++i) {
CachedAnnotation annot = readAnnotation(input);
annotations.put(annot.type, annot);
}
return new CachedAnnotations(annotations);
}
private void writeAnnotation(VarDataOutput output, AnnotationReader annotation) throws IOException {
output.writeUnsigned(symbolTable.lookup(annotation.getType()));
int fieldCount = 0;
for (@SuppressWarnings("unused") String field : annotation.getAvailableFields()) {
++fieldCount;
}
output.writeUnsigned(fieldCount);
for (String field : annotation.getAvailableFields()) {
output.writeUnsigned(symbolTable.lookup(field));
writeAnnotationValue(output, annotation.getValue(field));
}
}
private CachedAnnotation readAnnotation(VarDataInput input) throws IOException {
CachedAnnotation annotation = new CachedAnnotation();
annotation.type = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
int valueCount = input.readUnsigned();
Map<String, AnnotationValue> fields = new HashMap<>();
for (int i = 0; i < valueCount; ++i) {
String name = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
AnnotationValue value = readAnnotationValue(input);
fields.put(name, value);
}
annotation.fields = fields;
return annotation;
}
private void writeAnnotationValue(VarDataOutput output, AnnotationValue value) throws IOException {
output.writeUnsigned(value.getType());
switch (value.getType()) {
case AnnotationValue.ANNOTATION:
writeAnnotation(output, value.getAnnotation());
break;
case AnnotationValue.BOOLEAN:
output.writeUnsigned(value.getBoolean() ? 1 : 0);
break;
case AnnotationValue.BYTE:
output.writeSigned(value.getByte());
break;
case AnnotationValue.CLASS:
output.writeUnsigned(symbolTable.lookup(value.getJavaClass().toString()));
break;
case AnnotationValue.DOUBLE:
output.writeDouble(value.getDouble());
break;
case AnnotationValue.ENUM:
output.writeUnsigned(symbolTable.lookup(value.getEnumValue().getClassName()));
output.writeUnsigned(symbolTable.lookup(value.getEnumValue().getFieldName()));
break;
case AnnotationValue.FLOAT:
output.writeFloat(value.getFloat());
break;
case AnnotationValue.INT:
output.writeSigned(value.getInt());
break;
case AnnotationValue.LIST: {
List<AnnotationValue> list = value.getList();
output.writeUnsigned(list.size());
for (AnnotationValue item : list) {
writeAnnotationValue(output, item);
}
break;
}
case AnnotationValue.LONG:
output.writeSigned(value.getLong());
break;
case AnnotationValue.SHORT:
output.writeSigned(value.getShort());
break;
case AnnotationValue.STRING:
output.write(value.getString());
break;
}
}
private AnnotationValue readAnnotationValue(VarDataInput input) throws IOException {
int type = input.readUnsigned();
switch (type) {
case AnnotationValue.ANNOTATION:
return new AnnotationValue(readAnnotation(input));
case AnnotationValue.BOOLEAN:
return new AnnotationValue(input.readUnsigned() != 0);
case AnnotationValue.BYTE:
return new AnnotationValue((byte) input.readSigned());
case AnnotationValue.CLASS:
return new AnnotationValue(referenceCache.getCached(ValueType.parse(
symbolTable.at(input.readUnsigned()))));
case AnnotationValue.DOUBLE:
return new AnnotationValue(input.readDouble());
case AnnotationValue.ENUM: {
String className = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
String fieldName = referenceCache.getCached(symbolTable.at(input.readUnsigned()));
return new AnnotationValue(referenceCache.getCached(new FieldReference(className, fieldName)));
}
case AnnotationValue.FLOAT:
return new AnnotationValue(input.readFloat());
case AnnotationValue.INT:
return new AnnotationValue(input.readSigned());
case AnnotationValue.LIST: {
List<AnnotationValue> list = new ArrayList<>();
int sz = input.readUnsigned();
for (int i = 0; i < sz; ++i) {
list.add(readAnnotationValue(input));
}
return new AnnotationValue(list);
}
case AnnotationValue.LONG:
return new AnnotationValue(input.readSignedLong());
case AnnotationValue.SHORT:
return new AnnotationValue((short) input.readSigned());
case AnnotationValue.STRING:
return new AnnotationValue(input.read());
default:
throw new RuntimeException("Unexpected annotation value type: " + type);
}
}
private int packModifiers(Set<ElementModifier> modifiers) {
int result = 0;

View File

@ -80,7 +80,7 @@ public class MemoryCachedClassReaderSource implements ClassReaderSource, CacheSt
private Entry getEntry(String name) {
return cache.computeIfAbsent(name, className -> {
ClassReader cls = provider.apply(className);
ClassReader cls = provider != null ? provider.apply(className) : null;
Entry en = new Entry();
if (cls != null) {
ByteArrayOutputStream output = new ByteArrayOutputStream();

View File

@ -88,12 +88,14 @@ import org.teavm.model.instructions.SwitchInstruction;
import org.teavm.model.instructions.SwitchTableEntry;
import org.teavm.model.instructions.SwitchTableEntryReader;
import org.teavm.model.instructions.UnwrapArrayInstruction;
import org.teavm.model.util.ModelUtils;
public class ProgramIO {
private SymbolTable symbolTable;
private SymbolTable fileTable;
private SymbolTable variableTable;
private ReferenceCache referenceCache;
private AnnotationIO annotationIO;
private static BinaryOperation[] binaryOperations = BinaryOperation.values();
private static NumericOperandType[] numericOperandTypes = NumericOperandType.values();
private static IntegerSubtype[] integerSubtypes = IntegerSubtype.values();
@ -108,6 +110,7 @@ public class ProgramIO {
this.symbolTable = symbolTable;
this.fileTable = fileTable;
this.variableTable = variableTable;
annotationIO = new AnnotationIO(referenceCache, symbolTable);
}
public void write(ProgramReader program, OutputStream output) throws IOException {
@ -149,6 +152,7 @@ public class ProgramIO {
}
data.writeUnsigned(0);
}
annotationIO.writeAnnotations(data, program.getAnnotations());
}
public Program read(InputStream input) throws IOException {
@ -230,6 +234,7 @@ public class ProgramIO {
}
}
}
ModelUtils.copyAnnotations(annotationIO.readAnnotations(data), program.getAnnotations());
return program;
}

View File

@ -15,9 +15,9 @@
*/
package org.teavm.common;
import com.carrotsearch.hppc.IntArrayList;
import com.carrotsearch.hppc.IntHashSet;
import com.carrotsearch.hppc.IntSet;
import com.carrotsearch.hppc.cursors.IntCursor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -157,7 +157,8 @@ public class GraphIndexer {
List<WeightedNode> succList = new ArrayList<>(successors.length);
IntegerArray orderedSuccessors = new IntegerArray(successors.length);
if (terminalNodes.size() > 0) {
IntSet loopNodes = IntHashSet.from(findNaturalLoop(node, terminalNodes.getAll()));
int[] loopNodeList = findNaturalLoop(node, terminalNodes.getAll());
IntSet loopNodes = IntHashSet.from(loopNodeList);
for (int succ : successors) {
if (loopNodes.contains(succ)) {
succList.add(new WeightedNode(succ, priorities[succ], weights[succ]));
@ -170,8 +171,8 @@ public class GraphIndexer {
IntSet outerSuccessors = new IntHashSet(successors.length);
succList.clear();
for (IntCursor loopNode : loopNodes) {
for (int succ : graph.outgoingEdges(loopNode.value)) {
for (int loopNode : loopNodeList) {
for (int succ : graph.outgoingEdges(loopNode)) {
if (!loopNodes.contains(succ)) {
if (outerSuccessors.add(succ)) {
succList.add(new WeightedNode(succ, priorities[succ], weights[succ]));
@ -206,7 +207,9 @@ public class GraphIndexer {
private int[] findNaturalLoop(int head, int[] terminals) {
IntSet loop = new IntHashSet();
IntArrayList loopList = new IntArrayList();
loop.add(head);
loopList.add(head);
IntegerStack stack = new IntegerStack(1);
for (int pred : terminals) {
stack.push(pred);
@ -216,11 +219,12 @@ public class GraphIndexer {
if (!loop.add(node)) {
continue;
}
loopList.add(node);
for (int pred : graph.incomingEdges(node)) {
stack.push(pred);
}
}
return loop.toArray();
return loopList.toArray();
}
public int nodeAt(int index) {

View File

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

View File

@ -776,7 +776,8 @@ public abstract class DependencyAnalyzer implements DependencyInfo {
unprocessedClassSource = null;
classSource.innerHierarchy = null;
agentClassSource = classSourcePacker.pack(classSource, classSource.cache.keySet());
agentClassSource = classSourcePacker.pack(classSource,
ClassClosureAnalyzer.build(classSource, new ArrayList<>(classSource.cache.keySet())));
if (classSource != agentClassSource) {
classHierarchy = new ClassHierarchy(agentClassSource);
generatedClassNames.addAll(classSource.getGeneratedClassNames());

View File

@ -23,6 +23,7 @@ public class Program implements ProgramReader {
private List<Variable> variables = new ArrayList<>();
private boolean packed;
private int lastUsedRegister;
private AnnotationContainer annotations = new AnnotationContainer();
public BasicBlock createBasicBlock() {
BasicBlock block = new BasicBlock(this, basicBlocks.size());
@ -149,4 +150,9 @@ public class Program implements ProgramReader {
}
return variables.get(index);
}
@Override
public AnnotationContainer getAnnotations() {
return annotations;
}
}

View File

@ -25,4 +25,6 @@ public interface ProgramReader {
int variableCount();
VariableReader variableAt(int index);
AnnotationContainerReader getAnnotations();
}

View File

@ -16,7 +16,19 @@
package org.teavm.model.lowlevel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.teavm.model.AnnotationContainer;
import org.teavm.model.AnnotationContainerReader;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReader;
import org.teavm.model.Program;
public class CallSiteDescriptor {
private int id;
@ -39,4 +51,86 @@ public class CallSiteDescriptor {
public List<ExceptionHandlerDescriptor> getHandlers() {
return handlers;
}
public static void save(Collection<? extends CallSiteDescriptor> descriptors, AnnotationContainer annotations) {
List<AnnotationValue> descriptorsValue = new ArrayList<>();
for (CallSiteDescriptor descriptor : descriptors) {
AnnotationHolder descriptorAnnot = new AnnotationHolder(CallSiteDescriptorAnnot.class.getName());
descriptorAnnot.getValues().put("id", new AnnotationValue(descriptor.id));
descriptorAnnot.getValues().put("location", new AnnotationValue(descriptor.location.save()));
List<AnnotationValue> handlersValue = descriptor.handlers.stream()
.map(h -> new AnnotationValue(h.save()))
.collect(Collectors.toList());
descriptorAnnot.getValues().put("handlers", new AnnotationValue(handlersValue));
descriptorsValue.add(new AnnotationValue(descriptorAnnot));
}
AnnotationHolder descriptorsAnnot = new AnnotationHolder(CallSiteDescriptorsAnnot.class.getName());
descriptorsAnnot.getValues().put("value", new AnnotationValue(descriptorsValue));
annotations.add(descriptorsAnnot);
}
public static Collection<? extends CallSiteDescriptor> load(AnnotationContainerReader annotations) {
AnnotationReader descriptorsAnnot = annotations.get(CallSiteDescriptorsAnnot.class.getName());
if (descriptorsAnnot == null) {
return Collections.emptyList();
}
List<CallSiteDescriptor> descriptors = new ArrayList<>();
for (AnnotationValue descriptorValue : descriptorsAnnot.getValue("value").getList()) {
AnnotationReader descriptorAnnot = descriptorValue.getAnnotation();
int id = descriptorAnnot.getValue("id").getInt();
CallSiteLocation location = CallSiteLocation.load(descriptorAnnot.getValue("location").getAnnotation());
List<ExceptionHandlerDescriptor> handlers = descriptorAnnot.getValue("handlers").getList().stream()
.map(a -> ExceptionHandlerDescriptor.load(a.getAnnotation()))
.collect(Collectors.toList());
CallSiteDescriptor descriptor = new CallSiteDescriptor(id, location);
descriptor.getHandlers().addAll(handlers);
descriptors.add(descriptor);
}
return descriptors;
}
public static List<? extends CallSiteDescriptor> extract(Program program) {
List<CallSiteDescriptor> result = new ArrayList<>();
extractTo(load(program.getAnnotations()), result);
return result;
}
public static List<? extends CallSiteDescriptor> extract(ClassReaderSource classes,
Collection<? extends String> classNames) {
List<CallSiteDescriptor> result = new ArrayList<>();
for (String className : classNames) {
ClassReader cls = classes.get(className);
if (cls == null) {
continue;
}
for (MethodReader method : cls.getMethods()) {
if (method.getProgram() != null) {
extractTo(load(method.getProgram().getAnnotations()), result);
}
}
}
return result;
}
private static void extractTo(Collection<? extends CallSiteDescriptor> descriptors,
List<CallSiteDescriptor> result) {
for (CallSiteDescriptor descriptor : descriptors) {
if (descriptor.id >= result.size()) {
result.addAll(Collections.nCopies(descriptor.id - result.size() + 1, null));
}
result.set(descriptor.id, descriptor);
}
}
static AnnotationValue saveNullableString(String s) {
return new AnnotationValue(s != null ? "1" + s : "0");
}
static String loadNullableString(AnnotationValue value) {
String s = value.getString();
return s.startsWith("0") ? null : s.substring(1);
}
}

View File

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

View File

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

View File

@ -16,6 +16,9 @@
package org.teavm.model.lowlevel;
import java.util.Objects;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue;
public class CallSiteLocation {
private String fileName;
@ -63,4 +66,21 @@ public class CallSiteLocation {
public int hashCode() {
return Objects.hash(fileName, className, methodName, lineNumber);
}
public AnnotationReader save() {
AnnotationHolder annotation = new AnnotationHolder(CallSiteLocationAnnot.class.getName());
annotation.getValues().put("fileName", CallSiteDescriptor.saveNullableString(fileName));
annotation.getValues().put("className", CallSiteDescriptor.saveNullableString(className));
annotation.getValues().put("methodName", CallSiteDescriptor.saveNullableString(methodName));
annotation.getValues().put("lineNumber", new AnnotationValue(lineNumber));
return annotation;
}
public static CallSiteLocation load(AnnotationReader reader) {
return new CallSiteLocation(
CallSiteDescriptor.loadNullableString(reader.getValue("fileName")),
CallSiteDescriptor.loadNullableString(reader.getValue("className")),
CallSiteDescriptor.loadNullableString(reader.getValue("methodName")),
reader.getValue("lineNumber").getInt());
}
}

View File

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

View File

@ -15,6 +15,10 @@
*/
package org.teavm.model.lowlevel;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue;
public class ExceptionHandlerDescriptor {
private int id;
private String className;
@ -31,4 +35,17 @@ public class ExceptionHandlerDescriptor {
public String getClassName() {
return className;
}
public AnnotationReader save() {
AnnotationHolder annot = new AnnotationHolder(ExceptionHandlerDescriptorAnnot.class.getName());
annot.getValues().put("id", new AnnotationValue(id));
annot.getValues().put("className", CallSiteDescriptor.saveNullableString(className));
return annot;
}
public static ExceptionHandlerDescriptor load(AnnotationReader annot) {
return new ExceptionHandlerDescriptor(
annot.getValue("id").getInt(),
CallSiteDescriptor.loadNullableString(annot.getValue("className")));
}
}

View File

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

View File

@ -33,27 +33,26 @@ import org.teavm.model.instructions.JumpInstruction;
import org.teavm.runtime.ShadowStack;
public class ShadowStackTransformer {
private Characteristics managedMethodRepository;
private Characteristics characteristics;
private GCShadowStackContributor gcContributor;
private List<CallSiteDescriptor> callSites = new ArrayList<>();
public ShadowStackTransformer(Characteristics managedMethodRepository) {
gcContributor = new GCShadowStackContributor(managedMethodRepository);
this.managedMethodRepository = managedMethodRepository;
}
public List<CallSiteDescriptor> getCallSites() {
return callSites;
public ShadowStackTransformer(Characteristics characteristics) {
gcContributor = new GCShadowStackContributor(characteristics);
this.characteristics = characteristics;
}
public void apply(Program program, MethodReader method) {
if (!managedMethodRepository.isManaged(method.getReference())) {
if (!characteristics.isManaged(method.getReference())) {
return;
}
int shadowStackSize = gcContributor.contribute(program, method);
boolean exceptions = new ExceptionHandlingShadowStackContributor(managedMethodRepository, callSites,
int callSiteStartIndex = callSites.size();
boolean exceptions = new ExceptionHandlingShadowStackContributor(characteristics, callSites,
method.getReference(), program).contribute();
List<CallSiteDescriptor> programCallSites = callSites.subList(callSiteStartIndex, callSites.size());
CallSiteDescriptor.save(programCallSites, program.getAnnotations());
if (shadowStackSize > 0 || exceptions) {
addStackAllocation(program, shadowStackSize);

View File

@ -91,6 +91,7 @@ public final class ProgramUtils {
BasicBlock blockCopy = copy.basicBlockAt(i);
copyBasicBlock(block, blockCopy);
}
ModelUtils.copyAnnotations(program.getAnnotations(), copy.getAnnotations());
return copy;
}

View File

@ -342,6 +342,7 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
public void visit(GetFieldInstruction insn) {
String className = classNameMapper.apply(insn.getField().getClassName());
insn.setField(referenceCache.getCached(new FieldReference(className, insn.getField().getFieldName())));
insn.setFieldType(rename(insn.getFieldType()));
}
@Override
@ -350,6 +351,7 @@ public class ClassRefsRenamer extends AbstractInstructionVisitor {
if (className != insn.getField().getClassName()) {
insn.setField(referenceCache.getCached(new FieldReference(className, insn.getField().getFieldName())));
}
insn.setFieldType(rename(insn.getFieldType()));
}
@Override
public void visit(InvokeInstruction insn) {

View File

@ -26,13 +26,13 @@ public final class ExceptionHandling {
private ExceptionHandling() {
}
public static native CallSite findCallSiteById(int id);
public static native CallSite findCallSiteById(int id, Address frame);
public static void printStack() {
Address stackFrame = ShadowStack.getStackTop();
while (stackFrame != null) {
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId);
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
CallSiteLocation location = callSite.location;
Console.printString(" at ");
@ -74,7 +74,7 @@ public final class ExceptionHandling {
Address stackFrame = ShadowStack.getStackTop();
stackLoop: while (stackFrame != null) {
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId);
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
ExceptionHandler handler = callSite.firstHandler;
for (int i = 0; i < callSite.handlerCount; ++i) {
@ -118,7 +118,7 @@ public final class ExceptionHandling {
int index = 0;
while (stackFrame != null && index < target.length) {
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId);
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
CallSiteLocation location = callSite.location;
StackTraceElement element = createElement(location != null ? location.className : "",
location != null ? location.methodName : "", location != null ? location.fileName : null,

View File

@ -45,6 +45,8 @@ public class RuntimeClass extends RuntimeObject {
public IsSupertypeFunction isSupertypeOf;
public InitFunction init;
public RuntimeClass parent;
public int superinterfaceCount;
public RuntimeClassPointer superinterfaces;
public Address enumValues;
public Address layout;
public RuntimeObject simpleName;

View File

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

View File

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

View File

@ -16,7 +16,6 @@
package org.teavm.vm;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
@ -41,7 +40,7 @@ public class MemoryBuildTarget implements BuildTarget {
}
@Override
public OutputStream createResource(String fileName) throws IOException {
public OutputStream createResource(String fileName) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
data.put(fileName, stream);
return stream;

View File

@ -1,4 +1,3 @@
#pragma once
#include <stdint.h>
#include <uchar.h>
#include <stdlib.h>
@ -36,6 +35,8 @@ typedef struct TeaVM_Class {
int32_t (*isSupertypeOf)(struct TeaVM_Class*);
void (*init)();
struct TeaVM_Class* superclass;
int32_t superinterfaceCount;
struct TeaVM_Class** superinterfaces;
void* enumValues;
void* layout;
TeaVM_Object* simpleName;
@ -97,15 +98,35 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
return obj == NULL || cls(TEAVM_CLASS_OF(obj)) ? obj : teavm_throwClassCastException();
}
#define TEAVM_ALLOC_STACK(size) \
void* teavm__shadowStack__[(size) + 3]; \
teavm__shadowStack__[0] = teavm_stackTop; \
teavm__shadowStack__[2] = (void*) size; \
teavm_stackTop = teavm__shadowStack__
#ifdef TEAVM_INCREMENTAL
#define TEAVM_ALLOC_STACK_DEF(size, callSites) \
void* teavm__shadowStack__[(size) + 4]; \
teavm__shadowStack__[0] = teavm_stackTop; \
teavm__shadowStack__[2] = (void*) size; \
teavm__shadowStack__[3] = (void*) (callSites); \
teavm_stackTop = teavm__shadowStack__
#define TEAVM_STACK_HEADER_ADD_SIZE 1
#else
#define TEAVM_ALLOC_STACK(size) \
void* teavm__shadowStack__[(size) + 3]; \
teavm__shadowStack__[0] = teavm_stackTop; \
teavm__shadowStack__[2] = (void*) size; \
teavm_stackTop = teavm__shadowStack__
#define TEAVM_STACK_HEADER_ADD_SIZE 0
#endif
#define TEAVM_RELEASE_STACK teavm_stackTop = teavm__shadowStack__[0]
#define TEAVM_GC_ROOT(index, ptr) teavm__shadowStack__[3 + (index)] = ptr
#define TEAVM_GC_ROOT_RELEASE(index) teavm__shadowStack__[3 + (index)] = NULL
#define TEAVM_GC_ROOT(index, ptr) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = ptr
#define TEAVM_GC_ROOT_RELEASE(index) teavm__shadowStack__[3 + TEAVM_STACK_HEADER_ADD_SIZE + (index)] = NULL
#define TEAVM_GC_ROOTS_COUNT(ptr) ((int32_t) (intptr_t) ((void**) (ptr))[2])
#define TEAVM_GET_GC_ROOTS(ptr) (((void**) (ptr)) + 3 + TEAVM_STACK_HEADER_ADD_SIZE)
#define TEAVM_CALL_SITE(id) (teavm__shadowStack__[1] = (void*) (id))
#define TEAVM_EXCEPTION_HANDLER ((int32_t) (intptr_t) (teavm__shadowStack__[1]))
#define TEAVM_SET_EXCEPTION_HANDLER(frame, id) (((void**) (frame))[1] = (void*) (intptr_t) (id))
@ -113,6 +134,11 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
#define TEAVM_ADDRESS_ADD(address, offset) ((char *) (address) + (offset))
#define TEAVM_STRUCTURE_ADD(structure, address, offset) (((structure*) (address)) + offset)
#define TEAVM_NULL_STRING { \
.characters = NULL, \
.hashCode = 0 \
}
#define TEAVM_STRING(length, hash, s) { \
.characters = (TeaVM_Array*) & (struct { TeaVM_Array hdr; char16_t data[(length) + 1]; }) { \
.hdr = { .size = length }, \

View File

@ -89,6 +89,7 @@
<module>jso/impl</module>
<module>html4j</module>
<module>platform</module>
<module>tools/c-incremental</module>
<module>tools/core</module>
<module>tools/maven</module>
<module>tools/chrome-rdp</module>

View 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>

View File

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

View File

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

View File

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

View File

@ -56,6 +56,11 @@
<artifactId>teavm-devserver</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-c-incremental</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-jso-impl</artifactId>

View 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);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 Alexey Andreev.
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,22 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.devserver;
package org.teavm.tooling.builder;
import java.util.Collection;
import java.util.List;
import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.ProblemProvider;
import org.teavm.tooling.InstructionLocationReader;
import org.teavm.tooling.builder.BuildResult;
import org.teavm.vm.TeaVM;
class CodeServletBuildResult implements BuildResult {
public class SimpleBuildResult implements BuildResult {
private TeaVM vm;
private List<String> generatedFiles;
private Collection<String> usedResources;
public CodeServletBuildResult(TeaVM vm, List<String> generatedFiles) {
public SimpleBuildResult(TeaVM vm, List<String> generatedFiles) {
this.vm = vm;
this.generatedFiles = generatedFiles;
}

View File

@ -86,6 +86,7 @@ import org.teavm.parsing.resource.ResourceClassHolderMapper;
import org.teavm.tooling.EmptyTeaVMToolLog;
import org.teavm.tooling.TeaVMProblemRenderer;
import org.teavm.tooling.TeaVMToolLog;
import org.teavm.tooling.builder.SimpleBuildResult;
import org.teavm.tooling.util.FileSystemWatcher;
import org.teavm.vm.MemoryBuildTarget;
import org.teavm.vm.TeaVM;
@ -995,7 +996,7 @@ public class CodeServlet extends HttpServlet {
}
private void fireBuildComplete(TeaVM vm) {
CodeServletBuildResult result = new CodeServletBuildResult(vm, new ArrayList<>(buildTarget.getNames()));
SimpleBuildResult result = new SimpleBuildResult(vm, new ArrayList<>(buildTarget.getNames()));
for (DevServerListener listener : listeners) {
listener.compilationComplete(result);
}