mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C: add ability to write heap dump when application crashes
This commit is contained in:
parent
e0ce6d1df6
commit
492fd004af
|
@ -39,7 +39,6 @@ import org.teavm.backend.c.generate.BufferedCodeWriter;
|
|||
import org.teavm.backend.c.generate.CallSiteGenerator;
|
||||
import org.teavm.backend.c.generate.ClassGenerator;
|
||||
import org.teavm.backend.c.generate.CodeGenerationVisitor;
|
||||
import org.teavm.backend.c.generate.CodeGeneratorUtil;
|
||||
import org.teavm.backend.c.generate.CodeWriter;
|
||||
import org.teavm.backend.c.generate.GenerationContext;
|
||||
import org.teavm.backend.c.generate.IncludeManager;
|
||||
|
@ -159,6 +158,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
private boolean lineNumbersGenerated;
|
||||
private SimpleStringPool stringPool;
|
||||
private boolean longjmpUsed = true;
|
||||
private boolean heapDump;
|
||||
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
|
@ -177,6 +177,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
this.longjmpUsed = longjmpUsed;
|
||||
}
|
||||
|
||||
public void setHeapDump(boolean heapDump) {
|
||||
this.heapDump = heapDump;
|
||||
}
|
||||
|
||||
public void setAstCache(MethodNodeCache astCache) {
|
||||
this.astCache = astCache;
|
||||
}
|
||||
|
@ -239,6 +243,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
RuntimeClass.class, Address.class, int.class, RuntimeArray.class)).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(GC.class, "fixHeap", void.class)).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
|
||||
Throwable.class, void.class)).use();
|
||||
|
@ -362,7 +367,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
|
||||
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
|
||||
intrinsics, generators, asyncMethods::contains, buildTarget, incremental, longjmpUsed,
|
||||
vmAssertions);
|
||||
vmAssertions, vmAssertions || heapDump);
|
||||
|
||||
BufferedCodeWriter runtimeWriter = new BufferedCodeWriter(false);
|
||||
BufferedCodeWriter runtimeHeaderWriter = new BufferedCodeWriter(false);
|
||||
|
@ -378,6 +383,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
if (vmAssertions) {
|
||||
runtimeHeaderWriter.println("#define TEAVM_MEMORY_TRACE 1");
|
||||
}
|
||||
if (heapDump) {
|
||||
runtimeHeaderWriter.println("#define TEAVM_HEAP_DUMP 1");
|
||||
}
|
||||
emitResource(runtimeHeaderWriter, "runtime.h");
|
||||
|
||||
ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler,
|
||||
|
@ -404,6 +412,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
copyResource("references.c", buildTarget);
|
||||
copyResource("date.c", buildTarget);
|
||||
copyResource("file.c", buildTarget);
|
||||
copyResource("heap.c", buildTarget);
|
||||
generateCallSites(buildTarget, context, classes.getClassNames());
|
||||
generateStrings(buildTarget, context);
|
||||
|
||||
|
@ -508,46 +517,25 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
private void generateCallSites(BuildTarget buildTarget, GenerationContext context,
|
||||
Collection<? extends String> classNames) throws IOException {
|
||||
BufferedCodeWriter writer = new BufferedCodeWriter(false);
|
||||
BufferedCodeWriter headerWriter = new BufferedCodeWriter(false);
|
||||
|
||||
IncludeManager includes = new SimpleIncludeManager(writer);
|
||||
includes.init("callsites.c");
|
||||
includes.includePath("callsites.h");
|
||||
|
||||
headerWriter.println("#pragma once");
|
||||
IncludeManager headerIncludes = new SimpleIncludeManager(headerWriter);
|
||||
headerIncludes.init("callsites.h");
|
||||
headerIncludes.includePath("runtime.h");
|
||||
headerIncludes.includeClass(CallSiteGenerator.CALL_SITE);
|
||||
|
||||
if (incremental) {
|
||||
generateIncrementalCallSites(context, headerWriter);
|
||||
} else {
|
||||
generateFastCallSites(context, writer, includes, headerWriter, classNames);
|
||||
if (!incremental) {
|
||||
generateFastCallSites(context, writer, includes, 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)");
|
||||
|
||||
Collection<? extends String> classNames) {
|
||||
List<? extends CallSiteDescriptor> callSites = context.isLongjmp()
|
||||
? this.callSites
|
||||
: CallSiteDescriptor.extract(context.getClassSource(), classNames);
|
||||
new CallSiteGenerator(context, writer, includes, "teavm_callSites").generate(callSites);
|
||||
}
|
||||
|
||||
private void generateIncrementalCallSites(GenerationContext context, CodeWriter headerWriter) {
|
||||
String callSiteName = context.getNames().forClass(CallSiteGenerator.CALL_SITE);
|
||||
headerWriter.println("#define TEAVM_FIND_CALLSITE(id, frame) ((" + callSiteName
|
||||
+ "*) ((TeaVM_StackFrame*) (frame))->callSites + id)");
|
||||
}
|
||||
|
||||
private void generateStrings(BuildTarget buildTarget, GenerationContext context) throws IOException {
|
||||
BufferedCodeWriter writer = new BufferedCodeWriter(false);
|
||||
IncludeManager includes = new SimpleIncludeManager(writer);
|
||||
|
@ -709,6 +697,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
files.add("references.c");
|
||||
files.add("date.c");
|
||||
files.add("file.c");
|
||||
files.add("heap.c");
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
files.add(ClassGenerator.fileName(className) + ".c");
|
||||
|
@ -724,7 +713,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
|
||||
private void generateArrayOfClassReferences(GenerationContext context, CodeWriter writer, IncludeManager includes,
|
||||
List<? extends ValueType> types) {
|
||||
writer.print("static TeaVM_Class* teavm_classReferences[" + types.size() + "] = {").indent();
|
||||
writer.print("TeaVM_Class* teavm_classReferences[" + types.size() + "] = {").indent();
|
||||
boolean first = true;
|
||||
for (ValueType type : types) {
|
||||
if (!first) {
|
||||
|
@ -740,6 +729,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
writer.println();
|
||||
}
|
||||
writer.outdent().println("};");
|
||||
|
||||
writer.println("int32_t teavm_classReferencesCount = " + types.size() + ";");
|
||||
}
|
||||
|
||||
private void generateMain(GenerationContext context, CodeWriter writer, IncludeManager includes,
|
||||
|
@ -753,7 +744,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
|
||||
writer.println("teavm_beforeInit();");
|
||||
writer.println("teavm_initHeap(" + minHeapSize + ");");
|
||||
generateVirtualTableHeaders(context, writer, types);
|
||||
generateVirtualTableHeaders(context, writer);
|
||||
writer.println("teavm_initStringPool();");
|
||||
for (ValueType type : types) {
|
||||
includes.includeType(type);
|
||||
|
@ -798,23 +789,16 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
}
|
||||
}
|
||||
|
||||
private void generateVirtualTableHeaders(GenerationContext context, CodeWriter writer,
|
||||
List<? extends ValueType> types) {
|
||||
writer.println("teavm_beforeClasses = (char*) teavm_classReferences[0];");
|
||||
writer.println("for (int i = 1; i < " + types.size() + "; ++i) {").indent();
|
||||
writer.println("char* c = (char*) teavm_classReferences[i];");
|
||||
writer.println("if (c < teavm_beforeClasses) teavm_beforeClasses = c;");
|
||||
writer.outdent().println("}");
|
||||
writer.println("teavm_beforeClasses -= 4096;");
|
||||
|
||||
String classClassName = context.getNames().forClassInstance(ValueType.object("java.lang.Class"));
|
||||
writer.print("int32_t classHeader = TEAVM_PACK_CLASS(&" + classClassName + ") | ");
|
||||
CodeGeneratorUtil.writeIntValue(writer, RuntimeObject.GC_MARKED);
|
||||
writer.println(";");
|
||||
|
||||
writer.println("for (int i = 0; i < " + types.size() + "; ++i) {").indent();
|
||||
writer.println("teavm_classReferences[i]->parent.header = classHeader;");
|
||||
writer.outdent().println("}");
|
||||
private void generateVirtualTableHeaders(GenerationContext context, CodeWriter writer) {
|
||||
writer.println("teavm_classClass = (TeaVM_Class*) &" + context.getNames().forClassInstance(
|
||||
ValueType.object("java.lang.Class")) + ";");
|
||||
writer.println("teavm_objectClass = (TeaVM_Class*) &" + context.getNames().forClassInstance(
|
||||
ValueType.object("java.lang.Object")) + ";");
|
||||
writer.println("teavm_stringClass = (TeaVM_Class*) &" + context.getNames().forClassInstance(
|
||||
ValueType.object("java.lang.String")) + ";");
|
||||
writer.println("teavm_charArrayClass = (TeaVM_Class*) &" + context.getNames().forClassInstance(
|
||||
ValueType.arrayOf(ValueType.CHARACTER)) + ";");
|
||||
writer.println("teavm_initClasses();");
|
||||
}
|
||||
|
||||
private void generateFiberStart(GenerationContext context, CodeWriter writer, IncludeManager includes) {
|
||||
|
|
|
@ -20,17 +20,12 @@ import com.carrotsearch.hppc.ObjectIntMap;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.CallSiteLocation;
|
||||
import org.teavm.model.lowlevel.ExceptionHandlerDescriptor;
|
||||
|
||||
public class CallSiteGenerator {
|
||||
public static final String CALL_SITE = "org.teavm.runtime.CallSite";
|
||||
private static final String CALL_SITE_LOCATION = "org.teavm.runtime.CallSiteLocation";
|
||||
private static final String METHOD_LOCATION = "org.teavm.runtime.MethodLocation";
|
||||
private static final String EXCEPTION_HANDLER = "org.teavm.runtime.ExceptionHandler";
|
||||
|
||||
private GenerationContext context;
|
||||
private CodeWriter writer;
|
||||
|
@ -40,9 +35,6 @@ public class CallSiteGenerator {
|
|||
private List<HandlerContainer> exceptionHandlers = new ArrayList<>();
|
||||
private ObjectIntMap<MethodLocation> methodLocationMap = new ObjectIntHashMap<>();
|
||||
private List<MethodLocation> methodLocations = new ArrayList<>();
|
||||
private String callSiteLocationName;
|
||||
private String methodLocationName;
|
||||
private String exceptionHandlerName;
|
||||
private String callSitesName;
|
||||
private boolean isStatic;
|
||||
|
||||
|
@ -51,9 +43,6 @@ public class CallSiteGenerator {
|
|||
this.context = context;
|
||||
this.writer = writer;
|
||||
this.includes = includes;
|
||||
callSiteLocationName = context.getNames().forClass(CALL_SITE_LOCATION);
|
||||
methodLocationName = context.getNames().forClass(METHOD_LOCATION);
|
||||
exceptionHandlerName = context.getNames().forClass(EXCEPTION_HANDLER);
|
||||
this.callSitesName = callSitesName;
|
||||
}
|
||||
|
||||
|
@ -76,16 +65,11 @@ public class CallSiteGenerator {
|
|||
}
|
||||
|
||||
private void generateCallSites(List<? extends CallSiteDescriptor> callSites) {
|
||||
String callSiteName = context.getNames().forClass(CALL_SITE);
|
||||
|
||||
includes.includeClass(CALL_SITE);
|
||||
includes.includePath("strings.h");
|
||||
if (isStatic) {
|
||||
writer.print("static ");
|
||||
}
|
||||
writer.print(callSiteName).print(" " + callSitesName + "[" + callSites.size() + "] = {").indent();
|
||||
String firstHandlerName = fieldName(CALL_SITE, "firstHandler");
|
||||
String locationName = fieldName(CALL_SITE, "location");
|
||||
writer.print("TeaVM_CallSite " + callSitesName + "[" + callSites.size() + "] = {").indent();
|
||||
|
||||
boolean first = true;
|
||||
for (CallSiteDescriptor callSite : callSites) {
|
||||
|
@ -108,8 +92,8 @@ public class CallSiteGenerator {
|
|||
? "exceptionHandlers_" + callSitesName + " + " + exceptionHandlers.size()
|
||||
: "NULL";
|
||||
writer.println().print("{ ");
|
||||
writer.print(".").print(firstHandlerName).print(" = ").print(firstHandlerExpr).print(", ");
|
||||
writer.print(".").print(locationName).print(" = ")
|
||||
writer.print(".firstHandler = ").print(firstHandlerExpr).print(", ");
|
||||
writer.print(".location = ")
|
||||
.print(locationIndex >= 0 ? "callSiteLocations_" + callSitesName + " + " + locationIndex : "NULL");
|
||||
writer.print(" }");
|
||||
|
||||
|
@ -128,13 +112,9 @@ public class CallSiteGenerator {
|
|||
if (locations.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
includes.includeClass(CALL_SITE_LOCATION);
|
||||
writer.print("static ").print(callSiteLocationName).print(" callSiteLocations_" + callSitesName
|
||||
writer.print("static TeaVM_CallSiteLocation callSiteLocations_" + callSitesName
|
||||
+ "[" + locations.size() + "] = {").indent();
|
||||
|
||||
String methodName = fieldName(CALL_SITE_LOCATION, "method");
|
||||
String lineNumberName = fieldName(CALL_SITE_LOCATION, "lineNumber");
|
||||
|
||||
boolean first = true;
|
||||
for (CallSiteLocation location : locations) {
|
||||
if (!first) {
|
||||
|
@ -151,10 +131,9 @@ public class CallSiteGenerator {
|
|||
methodLocationMap.put(methodLocation, index);
|
||||
methodLocations.add(methodLocation);
|
||||
}
|
||||
writer.print(".").print(methodName).print(" = ").print("methodLocations_" + callSitesName + " + " + index)
|
||||
writer.print(".method = ").print("methodLocations_" + callSitesName + " + " + index)
|
||||
.print(", ");
|
||||
writer.print(".").print(lineNumberName).print(" = ")
|
||||
.print(String.valueOf(location.getLineNumber()));
|
||||
writer.print(".lineNumber = ").print(String.valueOf(location.getLineNumber()));
|
||||
|
||||
writer.print(" }");
|
||||
}
|
||||
|
@ -167,14 +146,9 @@ public class CallSiteGenerator {
|
|||
return;
|
||||
}
|
||||
|
||||
includes.includeClass(METHOD_LOCATION);
|
||||
writer.print("static ").print(methodLocationName).print(" methodLocations_" + callSitesName
|
||||
writer.print("static TeaVM_MethodLocation methodLocations_" + callSitesName
|
||||
+ "[" + methodLocations.size() + "] = {").indent();
|
||||
|
||||
String fileNameName = fieldName(METHOD_LOCATION, "fileName");
|
||||
String classNameName = fieldName(METHOD_LOCATION, "className");
|
||||
String methodNameName = fieldName(METHOD_LOCATION, "methodName");
|
||||
|
||||
boolean first = true;
|
||||
for (MethodLocation location : methodLocations) {
|
||||
if (!first) {
|
||||
|
@ -183,12 +157,9 @@ public class CallSiteGenerator {
|
|||
first = false;
|
||||
|
||||
writer.println().print("{ ");
|
||||
writer.print(".").print(fileNameName).print(" = ")
|
||||
.print(getStringExpr(location.file)).print(", ");
|
||||
writer.print(".").print(classNameName).print(" = ")
|
||||
.print(getStringExpr(location.className)).print(", ");
|
||||
writer.print(".").print(methodNameName).print(" = ")
|
||||
.print(getStringExpr(location.methodName));
|
||||
writer.print(".fileName = ").print(getStringExpr(location.file)).print(", ");
|
||||
writer.print(".className = ").print(getStringExpr(location.className)).print(", ");
|
||||
writer.print(".methodName = ").print(getStringExpr(location.methodName));
|
||||
|
||||
writer.print(" }");
|
||||
}
|
||||
|
@ -200,15 +171,10 @@ public class CallSiteGenerator {
|
|||
if (exceptionHandlers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
includes.includeClass(EXCEPTION_HANDLER);
|
||||
String name = "exceptionHandlers_" + callSitesName;
|
||||
writer.print("static ").print(exceptionHandlerName).print(" " + name + "["
|
||||
writer.print("static TeaVM_ExceptionHandler " + name + "["
|
||||
+ exceptionHandlers.size() + "] = {").indent();
|
||||
|
||||
String idName = fieldName(EXCEPTION_HANDLER, "id");
|
||||
String exceptionClassName = fieldName(EXCEPTION_HANDLER, "exceptionClass");
|
||||
String nextName = fieldName(EXCEPTION_HANDLER, "next");
|
||||
|
||||
boolean first = true;
|
||||
for (HandlerContainer handler : exceptionHandlers) {
|
||||
if (!first) {
|
||||
|
@ -222,12 +188,12 @@ public class CallSiteGenerator {
|
|||
includes.includeClass(handler.descriptor.getClassName());
|
||||
}
|
||||
String classExpr = handler.descriptor.getClassName() != null
|
||||
? "&" + context.getNames().forClassInstance(ValueType.object(handler.descriptor.getClassName()))
|
||||
? "(TeaVM_Class*) &"
|
||||
+ context.getNames().forClassInstance(ValueType.object(handler.descriptor.getClassName()))
|
||||
: "NULL";
|
||||
writer.print(".").print(idName).print(" = ").print(String.valueOf(handler.descriptor.getId())).print(", ");
|
||||
writer.print(".").print(exceptionClassName).print(" = ").print(classExpr).print(", ");
|
||||
writer.print(".").print(nextName).print(" = ")
|
||||
.print(handler.nextIndex < 0 ? "NULL" : name + "+" + handler.nextIndex);
|
||||
writer.print(".id = ").print(String.valueOf(handler.descriptor.getId())).print(", ");
|
||||
writer.print(".exceptionClass = ").print(classExpr).print(", ");
|
||||
writer.print(".next = ").print(handler.nextIndex < 0 ? "NULL" : name + "+" + handler.nextIndex);
|
||||
|
||||
writer.print(" }");
|
||||
}
|
||||
|
@ -235,10 +201,6 @@ public class CallSiteGenerator {
|
|||
writer.println().outdent().println("};");
|
||||
}
|
||||
|
||||
private String fieldName(String className, String fieldName) {
|
||||
return context.getNames().forMemberField(new FieldReference(className, fieldName));
|
||||
}
|
||||
|
||||
private String getStringExpr(String s) {
|
||||
return s != null ? "&TEAVM_GET_STRING(" + context.getStringPool().getStringIndex(s) + ")" : "NULL";
|
||||
}
|
||||
|
|
|
@ -661,6 +661,8 @@ public class ClassGenerator {
|
|||
tag = 0;
|
||||
sizeExpr = "0";
|
||||
itemTypeExpr = "NULL";
|
||||
flags |= RuntimeClass.PRIMITIVE;
|
||||
flags = ClassGeneratorUtil.applyPrimitiveFlags(flags, type);
|
||||
} else {
|
||||
parent = "NULL";
|
||||
tag = Integer.MAX_VALUE;
|
||||
|
@ -697,7 +699,167 @@ public class ClassGenerator {
|
|||
codeWriter.println(".superinterfaces = " + superinterfaces + ",");
|
||||
codeWriter.println(".layout = " + layout + ",");
|
||||
codeWriter.println(".enumValues = " + enumConstants + ",");
|
||||
codeWriter.println(".init = " + initFunction);
|
||||
codeWriter.print(".init = " + initFunction);
|
||||
|
||||
if (context.isHeapDump() && type instanceof ValueType.Object) {
|
||||
ClassReader cls = context.getClassSource().get(((ValueType.Object) type).getClassName());
|
||||
generateHeapDumpMetadata(cls);
|
||||
}
|
||||
codeWriter.println();
|
||||
}
|
||||
|
||||
private void generateHeapDumpMetadata(ClassReader cls) {
|
||||
List<HeapDumpField> fields = getHeapDumpFields(cls);
|
||||
List<HeapDumpField> staticFields = getHeapDumpStaticFields(cls);
|
||||
if (staticFields.isEmpty() && fields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
codeWriter.println().println("#if TEAVM_HEAP_DUMP").indent();
|
||||
if (!fields.isEmpty()) {
|
||||
codeWriter.println(",");
|
||||
codeWriter.println(".fieldDescriptors = (TeaVM_FieldDescriptors*) "
|
||||
+ "&(struct { uint32_t count; TeaVM_FieldDescriptor data["
|
||||
+ fields.size() + "]; }) {").indent();
|
||||
generateHeapDumpFields(fields);
|
||||
codeWriter.outdent().print("}");
|
||||
}
|
||||
if (!staticFields.isEmpty()) {
|
||||
codeWriter.println(",");
|
||||
codeWriter.println(".staticFieldDescriptors = (TeaVM_StaticFieldDescriptors*) "
|
||||
+ "&(struct { uint32_t count; TeaVM_StaticFieldDescriptor data["
|
||||
+ staticFields.size() + "]; }) {").indent();
|
||||
generateHeapDumpFields(staticFields);
|
||||
codeWriter.outdent().print("}");
|
||||
}
|
||||
codeWriter.println().outdent().println("#endif");
|
||||
}
|
||||
|
||||
private void generateHeapDumpFields(List<HeapDumpField> fields) {
|
||||
codeWriter.println(".count = " + fields.size() + ",");
|
||||
codeWriter.println(".data = {").indent();
|
||||
for (int i = 0; i < fields.size(); ++i) {
|
||||
if (i > 0) {
|
||||
codeWriter.println(",");
|
||||
}
|
||||
HeapDumpField field = fields.get(i);
|
||||
codeWriter.print("{ .name = u");
|
||||
StringPoolGenerator.generateSimpleStringLiteral(codeWriter, field.name);
|
||||
codeWriter.print(", .offset = " + field.offset + ", .type = " + field.type + " }");
|
||||
}
|
||||
codeWriter.println().outdent().println("}");
|
||||
}
|
||||
|
||||
private static final String TYPE_OBJECT = "TEAVM_FIELD_TYPE_OBJECT";
|
||||
|
||||
private List<HeapDumpField> getHeapDumpFields(ClassReader cls) {
|
||||
List<HeapDumpField> fields = new ArrayList<>();
|
||||
switch (cls.getName()) {
|
||||
case "java.lang.Object":
|
||||
case "java.lang.ref.ReferenceQueue":
|
||||
case "java.lang.ref.WeakReference":
|
||||
case "java.lang.ref.SoftReference":
|
||||
break;
|
||||
case "java.lang.Class":
|
||||
fields.add(new HeapDumpField("name", "offsetof(TeaVM_Class, name)", TYPE_OBJECT));
|
||||
fields.add(new HeapDumpField("simpleName", "offsetof(TeaVM_Class, simpleName)", TYPE_OBJECT));
|
||||
break;
|
||||
case "java.lang.ref.Reference":
|
||||
fields.add(new HeapDumpField("referent", "offsetof(TeaVM_Reference, object)", TYPE_OBJECT));
|
||||
fields.add(new HeapDumpField("queue", "offsetof(TeaVM_Reference, queue)", TYPE_OBJECT));
|
||||
break;
|
||||
default: {
|
||||
for (FieldReader field : cls.getFields()) {
|
||||
if (field.hasModifier(ElementModifier.STATIC) || !isManaged(field)) {
|
||||
continue;
|
||||
}
|
||||
String className = context.getNames().forClass(cls.getName());
|
||||
String offset = "offsetof(" + className + ", "
|
||||
+ context.getNames().forMemberField(field.getReference()) + ")";
|
||||
fields.add(new HeapDumpField(field.getName(), offset, typeForHeapDump(field.getType())));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
private List<HeapDumpField> getHeapDumpStaticFields(ClassReader cls) {
|
||||
List<HeapDumpField> fields = new ArrayList<>();
|
||||
switch (cls.getName()) {
|
||||
case "java.lang.Object":
|
||||
case "java.lang.Class":
|
||||
case "java.lang.ref.ReferenceQueue":
|
||||
case "java.lang.ref.Reference":
|
||||
case "java.lang.ref.WeakReference":
|
||||
case "java.lang.ref.SoftReference":
|
||||
break;
|
||||
default: {
|
||||
for (FieldReader field : cls.getFields()) {
|
||||
if (!field.hasModifier(ElementModifier.STATIC) || !isManaged(field)) {
|
||||
continue;
|
||||
}
|
||||
String offset = "(unsigned char*) &" + context.getNames().forStaticField(field.getReference());
|
||||
fields.add(new HeapDumpField(field.getName(), offset, typeForHeapDump(field.getType())));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
private boolean isManaged(FieldReader field) {
|
||||
ValueType type = field.getType();
|
||||
return !(type instanceof ValueType.Object)
|
||||
|| context.getCharacteristics().isManaged(((ValueType.Object) type).getClassName());
|
||||
}
|
||||
|
||||
static String typeForHeapDump(ValueType type) {
|
||||
String result = "127";
|
||||
if (type instanceof ValueType.Primitive) {
|
||||
switch (((ValueType.Primitive) type).getKind()) {
|
||||
case BOOLEAN:
|
||||
result = "TEAVM_FIELD_TYPE_BOOLEAN";
|
||||
break;
|
||||
case BYTE:
|
||||
result = "TEAVM_FIELD_TYPE_BYTE";
|
||||
break;
|
||||
case SHORT:
|
||||
result = "TEAVM_FIELD_TYPE_SHORT";
|
||||
break;
|
||||
case CHARACTER:
|
||||
result = "TEAVM_FIELD_TYPE_CHAR";
|
||||
break;
|
||||
case INTEGER:
|
||||
result = "TEAVM_FIELD_TYPE_INT";
|
||||
break;
|
||||
case FLOAT:
|
||||
result = "TEAVM_FIELD_TYPE_FLOAT";
|
||||
break;
|
||||
case LONG:
|
||||
result = "TEAVM_FIELD_TYPE_LONG";
|
||||
break;
|
||||
case DOUBLE:
|
||||
result = "TEAVM_FIELD_TYPE_DOUBLE";
|
||||
break;
|
||||
}
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
result = "TEAVM_FIELD_TYPE_ARRAY";
|
||||
} else {
|
||||
result = "TEAVM_FIELD_TYPE_OBJECT";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static class HeapDumpField {
|
||||
String name;
|
||||
String offset;
|
||||
String type;
|
||||
|
||||
HeapDumpField(String name, String offset, String type) {
|
||||
this.name = name;
|
||||
this.offset = offset;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
private void generateVirtualTableStructure(String className) {
|
||||
|
|
|
@ -46,12 +46,13 @@ public class GenerationContext {
|
|||
private boolean incremental;
|
||||
private boolean longjmp;
|
||||
private boolean vmAssertions;
|
||||
private boolean heapDump;
|
||||
|
||||
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, boolean incremental,
|
||||
boolean longjmp, boolean vmAssertions) {
|
||||
boolean longjmp, boolean vmAssertions, boolean heapDump) {
|
||||
this.virtualTableProvider = virtualTableProvider;
|
||||
this.characteristics = characteristics;
|
||||
this.dependencies = dependencies;
|
||||
|
@ -66,6 +67,7 @@ public class GenerationContext {
|
|||
this.incremental = incremental;
|
||||
this.longjmp = longjmp;
|
||||
this.vmAssertions = vmAssertions;
|
||||
this.heapDump = heapDump;
|
||||
}
|
||||
|
||||
public void addIntrinsic(Intrinsic intrinsic) {
|
||||
|
@ -134,6 +136,10 @@ public class GenerationContext {
|
|||
return longjmp;
|
||||
}
|
||||
|
||||
public boolean isHeapDump() {
|
||||
return heapDump;
|
||||
}
|
||||
|
||||
public boolean isVmAssertions() {
|
||||
return vmAssertions;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
|
|||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "findCallSiteById":
|
||||
context.includes().includePath("callsites.h");
|
||||
context.writer().print("TEAVM_FIND_CALLSITE(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(", ");
|
||||
|
|
|
@ -37,7 +37,7 @@ public final class GCVisualizer {
|
|||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length != 2) {
|
||||
System.err.println("Two arguments (input, ouput) expected");
|
||||
System.err.println("Two arguments (input, output) expected");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,912 @@
|
|||
/*
|
||||
* 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.backend.c.util;
|
||||
|
||||
import com.carrotsearch.hppc.LongArrayList;
|
||||
import com.carrotsearch.hppc.LongObjectHashMap;
|
||||
import com.carrotsearch.hppc.LongObjectMap;
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.teavm.backend.c.util.json.JsonAllErrorVisitor;
|
||||
import org.teavm.backend.c.util.json.JsonArrayVisitor;
|
||||
import org.teavm.backend.c.util.json.JsonErrorReporter;
|
||||
import org.teavm.backend.c.util.json.JsonObjectVisitor;
|
||||
import org.teavm.backend.c.util.json.JsonParser;
|
||||
import org.teavm.backend.c.util.json.JsonPropertyVisitor;
|
||||
import org.teavm.backend.c.util.json.JsonVisitingConsumer;
|
||||
import org.teavm.backend.c.util.json.JsonVisitor;
|
||||
|
||||
public final class HeapDumpConverter {
|
||||
private static byte[] buffer = new byte[8];
|
||||
private static int idSize;
|
||||
|
||||
private HeapDumpConverter() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length != 2) {
|
||||
System.err.println("Converts TeaVM/C heap dump into HotSpot compatible format (hprof)");
|
||||
System.err.println("Two arguments expected: input file (JSON), output file (hprof)");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
SymbolTable symbolTable;
|
||||
try (Reader reader = createReader(args[0])) {
|
||||
symbolTable = fillSymbolTable(reader);
|
||||
}
|
||||
|
||||
try (Reader reader = createReader(args[0]);
|
||||
RandomAccessFile output = new RandomAccessFile(args[1], "rw")) {
|
||||
generateHprofFile(reader, output, symbolTable);
|
||||
}
|
||||
}
|
||||
|
||||
private static Reader createReader(String fileName) throws IOException {
|
||||
return new InputStreamReader(new BufferedInputStream(new FileInputStream(fileName)), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static SymbolTable fillSymbolTable(Reader reader) throws IOException {
|
||||
SymbolTable symbolTable = new SymbolTable();
|
||||
JsonPropertyVisitor rootObjectVisitor = new JsonPropertyVisitor(true);
|
||||
rootObjectVisitor.addProperty("pointerSize", pointerSizeVisitor);
|
||||
rootObjectVisitor.addProperty("classes", new JsonArrayVisitor(new SymbolTableClassVisitor(symbolTable)));
|
||||
rootObjectVisitor.addProperty("stack", new JsonArrayVisitor(new SymbolTableStackVisitor(symbolTable)));
|
||||
JsonParser parser = new JsonParser(new JsonVisitingConsumer(new JsonObjectVisitor(rootObjectVisitor)));
|
||||
parser.parse(reader);
|
||||
|
||||
if (symbolTable.classLoaderClassId == 0) {
|
||||
addFakeClass(1, symbolTable, "java.lang.ClassLoader", symbolTable.objectClassId);
|
||||
}
|
||||
|
||||
if (symbolTable.referenceClassId == 0) {
|
||||
addFakeClass(2, symbolTable, "java.lang.ref.Reference", symbolTable.objectClassId);
|
||||
symbolTable.referenceClassId = 2;
|
||||
}
|
||||
if (symbolTable.weakReferenceClassId == 0) {
|
||||
addFakeClass(3, symbolTable, "java.lang.ref.WeakReference", symbolTable.referenceClassId);
|
||||
}
|
||||
if (symbolTable.softReferenceClassId == 0) {
|
||||
addFakeClass(4, symbolTable, "java.lang.ref.SoftReference", symbolTable.referenceClassId);
|
||||
}
|
||||
if (symbolTable.phantomReferenceClassId == 0) {
|
||||
addFakeClass(5, symbolTable, "java.lang.ref.PhantomReference", symbolTable.referenceClassId);
|
||||
}
|
||||
if (symbolTable.finalReferenceClassId == 0) {
|
||||
addFakeClass(6, symbolTable, "java.lang.ref.FinalReference", symbolTable.referenceClassId);
|
||||
}
|
||||
|
||||
fixClassNames(symbolTable);
|
||||
return symbolTable;
|
||||
}
|
||||
|
||||
private static void addFakeClass(long id, SymbolTable symbolTable, String name, long parent) {
|
||||
ClassDescriptor cls = new ClassDescriptor();
|
||||
cls.id = id;
|
||||
cls.superClassId = parent;
|
||||
cls.name = name;
|
||||
symbolTable.classesById.put(cls.id, cls);
|
||||
symbolTable.classList.add(cls);
|
||||
}
|
||||
|
||||
private static void fixClassNames(SymbolTable symbolTable) {
|
||||
int serialIdGen = 1;
|
||||
int anonymousIdGen = 0;
|
||||
|
||||
for (ClassDescriptor descriptor : symbolTable.classList) {
|
||||
if (descriptor.primitiveType != null) {
|
||||
continue;
|
||||
}
|
||||
descriptor.serialId = serialIdGen++;
|
||||
|
||||
if (descriptor.itemClassId != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (descriptor.name == null) {
|
||||
do {
|
||||
descriptor.name = "anonymous_" + anonymousIdGen++;
|
||||
} while (symbolTable.getClass(descriptor.name) != null);
|
||||
} else {
|
||||
descriptor.name = descriptor.name.replace('.', '/');
|
||||
}
|
||||
}
|
||||
|
||||
List<ClassDescriptor> arrayClasses = new ArrayList<>();
|
||||
for (ClassDescriptor descriptor : symbolTable.classList) {
|
||||
if (descriptor.itemClassId == 0 || descriptor.name != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (descriptor.itemClassId != 0) {
|
||||
arrayClasses.add(descriptor);
|
||||
descriptor = symbolTable.classesById.get(descriptor.itemClassId);
|
||||
if (descriptor.name != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String name = descriptor.name;
|
||||
if (descriptor.primitiveType != null) {
|
||||
switch (descriptor.primitiveType) {
|
||||
case BOOLEAN:
|
||||
name = "Z";
|
||||
break;
|
||||
case BYTE:
|
||||
name = "B";
|
||||
break;
|
||||
case SHORT:
|
||||
name = "S";
|
||||
break;
|
||||
case CHAR:
|
||||
name = "C";
|
||||
break;
|
||||
case INT:
|
||||
name = "I";
|
||||
break;
|
||||
case LONG:
|
||||
name = "J";
|
||||
break;
|
||||
case FLOAT:
|
||||
name = "F";
|
||||
break;
|
||||
case DOUBLE:
|
||||
name = "D";
|
||||
break;
|
||||
case OBJECT:
|
||||
case ARRAY:
|
||||
assert false;
|
||||
break;
|
||||
}
|
||||
} else if (descriptor.itemClassId == 0) {
|
||||
name = "L" + name + ";";
|
||||
}
|
||||
|
||||
for (int i = arrayClasses.size() - 1; i >= 0; --i) {
|
||||
name = "[" + name;
|
||||
arrayClasses.get(i).name = name;
|
||||
}
|
||||
|
||||
arrayClasses.clear();
|
||||
}
|
||||
|
||||
for (ClassDescriptor descriptor : symbolTable.classList) {
|
||||
if (descriptor.name != null) {
|
||||
symbolTable.lookup(descriptor.name);
|
||||
symbolTable.classes.put(descriptor.name, descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonVisitor pointerSizeVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
idSize = (int) value;
|
||||
}
|
||||
};
|
||||
|
||||
private static void generateHprofFile(Reader reader, RandomAccessFile output, SymbolTable symbolTable)
|
||||
throws IOException {
|
||||
output.write("JAVA PROFILE 1.0.2\0".getBytes(StandardCharsets.UTF_8));
|
||||
output.writeInt(idSize);
|
||||
output.writeLong(System.currentTimeMillis());
|
||||
writeSymbols(output, symbolTable);
|
||||
writeStack(output, symbolTable);
|
||||
writeHeapDump(reader, output, symbolTable);
|
||||
|
||||
output.write(0x2C);
|
||||
output.writeInt(0);
|
||||
output.writeInt(0);
|
||||
output.setLength(output.getFilePointer());
|
||||
}
|
||||
|
||||
private static void writeSymbols(RandomAccessFile output, SymbolTable symbolTable) throws IOException {
|
||||
List<String> strings = symbolTable.getStrings();
|
||||
for (int i = 0; i < strings.size(); ++i) {
|
||||
byte[] bytes = strings.get(i).getBytes(StandardCharsets.UTF_8);
|
||||
output.write(1);
|
||||
output.writeInt(0);
|
||||
output.writeInt(idSize + bytes.length);
|
||||
|
||||
writeId(output, i + 1);
|
||||
output.write(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeStack(RandomAccessFile output, SymbolTable symbolTable) throws IOException {
|
||||
for (int i = 0; i < symbolTable.stack.size(); ++i) {
|
||||
Frame frame = symbolTable.stack.get(i);
|
||||
output.write(4);
|
||||
output.writeInt(0);
|
||||
output.writeInt(4 * idSize + 8);
|
||||
writeId(output, i + 1);
|
||||
writeId(output, frame.methodName != null ? symbolTable.lookup(frame.methodName) : 0);
|
||||
writeId(output, 0);
|
||||
writeId(output, frame.fileName != null ? symbolTable.lookup(frame.fileName) : 0);
|
||||
int classSerialId = 0;
|
||||
if (frame.className != null) {
|
||||
ClassDescriptor cls = symbolTable.getClass(frame.className);
|
||||
if (cls != null) {
|
||||
classSerialId = cls.serialId;
|
||||
}
|
||||
}
|
||||
output.writeInt(classSerialId);
|
||||
output.writeInt(frame.lineNumber);
|
||||
}
|
||||
|
||||
output.write(5);
|
||||
output.writeInt(0);
|
||||
output.writeInt(12 + idSize * symbolTable.stack.size());
|
||||
|
||||
output.writeInt(1);
|
||||
output.writeInt(0);
|
||||
output.writeInt(symbolTable.stack.size());
|
||||
for (int i = 0; i < symbolTable.stack.size(); ++i) {
|
||||
writeId(output, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeGcRoots(RandomAccessFile output, SymbolTable symbolTable) throws IOException {
|
||||
List<Frame> stack = symbolTable.stack;
|
||||
for (int i = 0; i < stack.size(); i++) {
|
||||
Frame frame = stack.get(i);
|
||||
if (frame.roots == null) {
|
||||
continue;
|
||||
}
|
||||
for (long rootId : frame.roots) {
|
||||
output.write(3);
|
||||
writeId(output, rootId);
|
||||
output.writeInt(0);
|
||||
output.writeInt(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (ClassDescriptor classDescriptor : symbolTable.getClasses()) {
|
||||
if (classDescriptor.primitiveType != null) {
|
||||
continue;
|
||||
}
|
||||
output.write(5);
|
||||
writeId(output, classDescriptor.id);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeHeapDump(Reader reader, RandomAccessFile output, SymbolTable symbolTable)
|
||||
throws IOException {
|
||||
for (ClassDescriptor classDescriptor : symbolTable.getClasses()) {
|
||||
if (classDescriptor.primitiveType != null) {
|
||||
continue;
|
||||
}
|
||||
output.write(2);
|
||||
output.writeInt(0);
|
||||
output.writeInt(8 + 2 * idSize);
|
||||
output.writeInt(classDescriptor.serialId);
|
||||
writeId(output, classDescriptor.id);
|
||||
output.writeInt(1);
|
||||
writeId(output, symbolTable.lookup(classDescriptor.name));
|
||||
}
|
||||
|
||||
output.write(0x0C);
|
||||
output.writeInt(0);
|
||||
output.writeInt(0);
|
||||
long mark = output.getFilePointer();
|
||||
|
||||
writeGcRoots(output, symbolTable);
|
||||
writeClassObjects(output, symbolTable);
|
||||
|
||||
JsonPropertyVisitor rootPropertyVisitor = new JsonPropertyVisitor(true);
|
||||
rootPropertyVisitor.addProperty("objects", new JsonArrayVisitor(new ObjectDumpVisitor(output, symbolTable)));
|
||||
JsonParser parser = new JsonParser(new JsonVisitingConsumer(new JsonObjectVisitor(rootPropertyVisitor)));
|
||||
parser.parse(reader);
|
||||
|
||||
long pointerBackup = output.getFilePointer();
|
||||
int size = (int) (output.getFilePointer() - mark);
|
||||
output.seek(mark - 4);
|
||||
output.writeInt(size);
|
||||
output.seek(pointerBackup);
|
||||
}
|
||||
|
||||
private static void writeClassObjects(RandomAccessFile output, SymbolTable symbolTable) throws IOException {
|
||||
Collection<ClassDescriptor> classes = symbolTable.getClasses();
|
||||
for (ClassDescriptor cls : classes) {
|
||||
if (cls.primitiveType == null) {
|
||||
writeClassDump(output, symbolTable, cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeClassDump(RandomAccessFile output, SymbolTable symbolTable, ClassDescriptor cls)
|
||||
throws IOException {
|
||||
output.write(0x20);
|
||||
writeId(output, cls.id);
|
||||
output.writeInt(1);
|
||||
|
||||
long superClassId = cls.superClassId;
|
||||
if (cls.itemClassId != 0) {
|
||||
superClassId = symbolTable.objectClassId;
|
||||
}
|
||||
writeId(output, superClassId);
|
||||
writeId(output, 0);
|
||||
writeId(output, 0);
|
||||
writeId(output, 0);
|
||||
writeId(output, 0);
|
||||
writeId(output, 0);
|
||||
|
||||
output.writeInt(cls.size);
|
||||
output.writeShort((short) 0);
|
||||
|
||||
output.writeShort((short) cls.staticFields.size());
|
||||
int dataPtr = 0;
|
||||
for (FieldDescriptor field : cls.staticFields) {
|
||||
writeId(output, symbolTable.lookup(field.name));
|
||||
output.write(typeToInt(field.type));
|
||||
int sz = typeSize(field.type);
|
||||
output.write(cls.data, dataPtr, sz);
|
||||
dataPtr += sz;
|
||||
}
|
||||
|
||||
output.writeShort((short) cls.fields.size());
|
||||
for (FieldDescriptor field : cls.fields) {
|
||||
writeId(output, symbolTable.lookup(field.name));
|
||||
output.write(typeToInt(field.type));
|
||||
}
|
||||
}
|
||||
|
||||
static class ObjectDumpVisitor extends JsonAllErrorVisitor {
|
||||
private DataOutput output;
|
||||
private SymbolTable symbolTable;
|
||||
private JsonPropertyVisitor propertyVisitor = new JsonPropertyVisitor(true);
|
||||
private long id;
|
||||
private long classId;
|
||||
private byte[] data;
|
||||
|
||||
ObjectDumpVisitor(DataOutput output, SymbolTable symbolTable) {
|
||||
this.output = output;
|
||||
this.symbolTable = symbolTable;
|
||||
propertyVisitor.addProperty("id", idVisitor);
|
||||
propertyVisitor.addProperty("class", classVisitor);
|
||||
propertyVisitor.addProperty("data", dataVisitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonVisitor object(JsonErrorReporter reporter) {
|
||||
id = 0;
|
||||
classId = 0;
|
||||
data = null;
|
||||
return propertyVisitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(JsonErrorReporter reporter) {
|
||||
try {
|
||||
ClassDescriptor cls = symbolTable.getClassById(classId);
|
||||
if (cls == null) {
|
||||
reporter.error("Unknown class: " + classId);
|
||||
}
|
||||
if (cls.itemClassId == 0) {
|
||||
output.write(0x21);
|
||||
writeId(output, id);
|
||||
output.writeInt(1);
|
||||
writeId(output, classId);
|
||||
output.writeInt(data.length);
|
||||
int dataPtr = data.length;
|
||||
while (cls != null) {
|
||||
for (FieldDescriptor field : cls.fields) {
|
||||
dataPtr -= typeSize(field.type);
|
||||
}
|
||||
int ptr = dataPtr;
|
||||
for (FieldDescriptor field : cls.fields) {
|
||||
int size = typeSize(field.type);
|
||||
output.write(data, ptr, size);
|
||||
ptr += size;
|
||||
}
|
||||
cls = cls.superClassId != 0 ? symbolTable.getClassById(cls.superClassId) : null;
|
||||
}
|
||||
} else {
|
||||
ClassDescriptor itemCls = symbolTable.getClassById(cls.itemClassId);
|
||||
output.write(itemCls.primitiveType == null ? 0x22 : 0x23);
|
||||
writeId(output, id);
|
||||
output.writeInt(1);
|
||||
int itemSize = itemCls.primitiveType != null ? typeSize(itemCls.primitiveType) : idSize;
|
||||
int size = data.length / itemSize;
|
||||
output.writeInt(size);
|
||||
if (itemCls.primitiveType == null) {
|
||||
writeId(output, classId);
|
||||
} else {
|
||||
output.writeByte(typeToInt(itemCls.primitiveType));
|
||||
}
|
||||
for (int i = 0; i < size; ++i) {
|
||||
int ptr = i * itemSize;
|
||||
output.write(data, ptr, itemSize);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private JsonVisitor idVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
id = value;
|
||||
}
|
||||
};
|
||||
|
||||
private JsonVisitor classVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
classId = value;
|
||||
}
|
||||
};
|
||||
|
||||
private JsonVisitor dataVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
data = parseData(reporter, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static class SymbolTableClassVisitor extends JsonAllErrorVisitor {
|
||||
SymbolTable symbolTable;
|
||||
ClassDescriptor descriptor;
|
||||
FieldDescriptor fieldDescriptor;
|
||||
private JsonPropertyVisitor propertyVisitor;
|
||||
private List<FieldDescriptor> fields;
|
||||
|
||||
SymbolTableClassVisitor(SymbolTable symbolTable) {
|
||||
this.symbolTable = symbolTable;
|
||||
propertyVisitor = new JsonPropertyVisitor(true);
|
||||
propertyVisitor.addProperty("id", idVisitor);
|
||||
propertyVisitor.addProperty("name", nameVisitor);
|
||||
propertyVisitor.addProperty("super", superVisitor);
|
||||
propertyVisitor.addProperty("size", sizeVisitor);
|
||||
propertyVisitor.addProperty("primitive", primitiveVisitor);
|
||||
propertyVisitor.addProperty("item", itemVisitor);
|
||||
propertyVisitor.addProperty("fields", fieldsVisitor);
|
||||
propertyVisitor.addProperty("staticFields", staticFieldsVisitor);
|
||||
propertyVisitor.addProperty("data", dataVisitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonVisitor object(JsonErrorReporter reporter) {
|
||||
descriptor = new ClassDescriptor();
|
||||
return propertyVisitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(JsonErrorReporter reporter) {
|
||||
if (descriptor.id == 0) {
|
||||
reporter.error("Required property 'id' was not set");
|
||||
}
|
||||
symbolTable.classList.add(descriptor);
|
||||
if (symbolTable.classesById.put(descriptor.id, descriptor) != null) {
|
||||
reporter.error("Duplicate class id: " + descriptor.id);
|
||||
}
|
||||
if (descriptor.name != null) {
|
||||
switch (descriptor.name) {
|
||||
case "java.lang.Object":
|
||||
symbolTable.objectClassId = descriptor.id;
|
||||
break;
|
||||
case "java.lang.ClassLoader":
|
||||
symbolTable.classLoaderClassId = descriptor.id;
|
||||
break;
|
||||
case "java.lang.ref.Reference":
|
||||
symbolTable.referenceClassId = descriptor.id;
|
||||
break;
|
||||
case "java.lang.ref.WeakReference":
|
||||
symbolTable.weakReferenceClassId = descriptor.id;
|
||||
break;
|
||||
case "java.lang.ref.SoftReference":
|
||||
symbolTable.softReferenceClassId = descriptor.id;
|
||||
break;
|
||||
case "java.lang.ref.PhantomReference":
|
||||
symbolTable.phantomReferenceClassId = descriptor.id;
|
||||
break;
|
||||
case "java.lang.ref.FinalReference":
|
||||
symbolTable.finalReferenceClassId = descriptor.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JsonVisitor idVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
descriptor.id = value;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor nameVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
descriptor.name = value;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor superVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
descriptor.superClassId = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullValue(JsonErrorReporter reporter) {
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor sizeVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
descriptor.size = (int) value;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor primitiveVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
descriptor.primitiveType = parseType(reporter, value);
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor itemVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
descriptor.itemClassId = value;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor fieldsVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public JsonVisitor array(JsonErrorReporter reporter) {
|
||||
fields = descriptor.fields;
|
||||
return fieldVisitor;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor staticFieldsVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public JsonVisitor array(JsonErrorReporter reporter) {
|
||||
fields = descriptor.staticFields;
|
||||
return fieldVisitor;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor fieldNameVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
fieldDescriptor.name = value;
|
||||
symbolTable.lookup(value);
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor fieldTypeVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
fieldDescriptor.type = parseType(reporter, value);
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor fieldVisitor = new JsonAllErrorVisitor() {
|
||||
private JsonPropertyVisitor propertyVisitor = new JsonPropertyVisitor(true);
|
||||
|
||||
{
|
||||
propertyVisitor.addProperty("name", fieldNameVisitor);
|
||||
propertyVisitor.addProperty("type", fieldTypeVisitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonVisitor object(JsonErrorReporter reporter) {
|
||||
fieldDescriptor = new FieldDescriptor();
|
||||
return propertyVisitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(JsonErrorReporter reporter) {
|
||||
if (fieldDescriptor.type == null) {
|
||||
reporter.error("Type for this field not specified");
|
||||
}
|
||||
fields.add(fieldDescriptor);
|
||||
fieldDescriptor = null;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor dataVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
descriptor.data = parseData(reporter, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Type parseType(JsonErrorReporter errorReporter, String type) {
|
||||
switch (type) {
|
||||
case "object":
|
||||
return Type.OBJECT;
|
||||
case "array":
|
||||
return Type.ARRAY;
|
||||
case "boolean":
|
||||
return Type.BOOLEAN;
|
||||
case "byte":
|
||||
return Type.BYTE;
|
||||
case "char":
|
||||
return Type.CHAR;
|
||||
case "short":
|
||||
return Type.SHORT;
|
||||
case "int":
|
||||
return Type.INT;
|
||||
case "long":
|
||||
return Type.LONG;
|
||||
case "float":
|
||||
return Type.FLOAT;
|
||||
case "double":
|
||||
return Type.DOUBLE;
|
||||
default:
|
||||
errorReporter.error("Unknown type: " + type);
|
||||
return Type.OBJECT;
|
||||
}
|
||||
}
|
||||
|
||||
static class SymbolTableStackVisitor extends JsonAllErrorVisitor {
|
||||
SymbolTable symbolTable;
|
||||
private JsonPropertyVisitor propertyVisitor = new JsonPropertyVisitor(true);
|
||||
private Frame frame;
|
||||
private LongArrayList roots = new LongArrayList();
|
||||
|
||||
SymbolTableStackVisitor(SymbolTable symbolTable) {
|
||||
this.symbolTable = symbolTable;
|
||||
propertyVisitor.addProperty("file", fileVisitor);
|
||||
propertyVisitor.addProperty("class", classVisitor);
|
||||
propertyVisitor.addProperty("method", methodVisitor);
|
||||
propertyVisitor.addProperty("line", lineVisitor);
|
||||
propertyVisitor.addProperty("roots", new JsonArrayVisitor(rootsVisitor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonVisitor object(JsonErrorReporter reporter) {
|
||||
frame = new Frame();
|
||||
return propertyVisitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(JsonErrorReporter reporter) {
|
||||
if (!roots.isEmpty()) {
|
||||
frame.roots = roots.toArray();
|
||||
roots.clear();
|
||||
}
|
||||
symbolTable.stack.add(frame);
|
||||
}
|
||||
|
||||
JsonVisitor fileVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
symbolTable.lookup(value);
|
||||
frame.fileName = value;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor classVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
symbolTable.lookup(value);
|
||||
frame.className = value;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor methodVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
symbolTable.lookup(value);
|
||||
frame.methodName = value;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor lineVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
frame.lineNumber = (int) value;
|
||||
}
|
||||
};
|
||||
|
||||
JsonVisitor rootsVisitor = new JsonAllErrorVisitor() {
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
roots.add(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static class Frame {
|
||||
String className;
|
||||
String methodName;
|
||||
String fileName;
|
||||
int lineNumber = -1;
|
||||
long[] roots;
|
||||
}
|
||||
|
||||
static class ClassDescriptor {
|
||||
long id;
|
||||
String name;
|
||||
int serialId;
|
||||
Type primitiveType;
|
||||
long itemClassId;
|
||||
long superClassId;
|
||||
int size;
|
||||
List<FieldDescriptor> fields = new ArrayList<>();
|
||||
List<FieldDescriptor> staticFields = new ArrayList<>();
|
||||
byte[] data;
|
||||
}
|
||||
|
||||
static class FieldDescriptor {
|
||||
String name;
|
||||
Type type;
|
||||
}
|
||||
|
||||
enum Type {
|
||||
OBJECT,
|
||||
ARRAY,
|
||||
BOOLEAN,
|
||||
BYTE,
|
||||
CHAR,
|
||||
SHORT,
|
||||
INT,
|
||||
LONG,
|
||||
FLOAT,
|
||||
DOUBLE
|
||||
}
|
||||
|
||||
private static int typeToInt(Type type) {
|
||||
switch (type) {
|
||||
case OBJECT:
|
||||
case ARRAY:
|
||||
return 2;
|
||||
case BOOLEAN:
|
||||
return 4;
|
||||
case CHAR:
|
||||
return 5;
|
||||
case FLOAT:
|
||||
return 6;
|
||||
case DOUBLE:
|
||||
return 7;
|
||||
case BYTE:
|
||||
return 8;
|
||||
case SHORT:
|
||||
return 9;
|
||||
case INT:
|
||||
return 10;
|
||||
case LONG:
|
||||
return 11;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int typeSize(Type type) {
|
||||
switch (type) {
|
||||
case OBJECT:
|
||||
case ARRAY:
|
||||
return idSize;
|
||||
case BYTE:
|
||||
case BOOLEAN:
|
||||
return 1;
|
||||
case CHAR:
|
||||
case SHORT:
|
||||
return 2;
|
||||
case INT:
|
||||
case FLOAT:
|
||||
return 4;
|
||||
case LONG:
|
||||
case DOUBLE:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class SymbolTable {
|
||||
private List<String> strings = new ArrayList<>();
|
||||
private ObjectIntMap<String> stringIndexes = new ObjectIntHashMap<>();
|
||||
List<ClassDescriptor> classList = new ArrayList<>();
|
||||
Map<String, ClassDescriptor> classes = new LinkedHashMap<>();
|
||||
LongObjectMap<ClassDescriptor> classesById = new LongObjectHashMap<>();
|
||||
List<Frame> stack = new ArrayList<>();
|
||||
long objectClassId;
|
||||
long classLoaderClassId;
|
||||
long referenceClassId;
|
||||
long weakReferenceClassId;
|
||||
long softReferenceClassId;
|
||||
long finalReferenceClassId;
|
||||
long phantomReferenceClassId;
|
||||
|
||||
List<String> getStrings() {
|
||||
return strings;
|
||||
}
|
||||
|
||||
ClassDescriptor getClass(String name) {
|
||||
return classes.get(name);
|
||||
}
|
||||
|
||||
ClassDescriptor getClassById(long id) {
|
||||
return classesById.get(id);
|
||||
}
|
||||
|
||||
Collection<ClassDescriptor> getClasses() {
|
||||
return classList;
|
||||
}
|
||||
|
||||
int lookup(String str) {
|
||||
int value = stringIndexes.getOrDefault(str, -1);
|
||||
if (value < 0) {
|
||||
value = strings.size() + 1;
|
||||
strings.add(str);
|
||||
stringIndexes.put(str, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeId(DataOutput out, long id) throws IOException {
|
||||
writeLongBytes(out, id, idSize);
|
||||
}
|
||||
|
||||
private static void writeLongBytes(DataOutput out, long v, int size) throws IOException {
|
||||
for (int i = size - 1; i >= 0; --i) {
|
||||
buffer[i] = (byte) (v & 255);
|
||||
v >>>= 8;
|
||||
}
|
||||
|
||||
out.write(buffer, 0, size);
|
||||
}
|
||||
|
||||
static byte[] parseData(JsonErrorReporter errorReporter, String data) {
|
||||
if (data.length() % 2 != 0) {
|
||||
errorReporter.error("Invalid hex sequence");
|
||||
return new byte[0];
|
||||
}
|
||||
byte[] bytes = new byte[data.length() / 2];
|
||||
int j = 0;
|
||||
for (int i = 0; i < data.length(); i += 2) {
|
||||
int b = (hexDigit(errorReporter, data.charAt(i)) << 4) | hexDigit(errorReporter, data.charAt(i + 1));
|
||||
bytes[j++] = (byte) b;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static int hexDigit(JsonErrorReporter errorReporter, char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
return c - 'A' + 10;
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
} else {
|
||||
errorReporter.error("Invalid hex sequence");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.backend.c.util.json;
|
||||
|
||||
public class JsonAllErrorVisitor extends JsonVisitor {
|
||||
@Override
|
||||
public JsonVisitor object(JsonErrorReporter reporter) {
|
||||
reporter.error("Unexpected object");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonVisitor array(JsonErrorReporter reporter) {
|
||||
reporter.error("Unexpected array");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonVisitor property(JsonErrorReporter reporter, String name) {
|
||||
reporter.error("Unexpected property");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
reporter.error("Unexpected string");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
reporter.error("Unexpected number");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void floatValue(JsonErrorReporter reporter, double value) {
|
||||
reporter.error("Unexpected number");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullValue(JsonErrorReporter reporter) {
|
||||
reporter.error("Unexpected null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void booleanValue(JsonErrorReporter reporter, boolean value) {
|
||||
reporter.error("Unexpected boolean");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.backend.c.util.json;
|
||||
|
||||
public class JsonArrayVisitor extends JsonAllErrorVisitor {
|
||||
private JsonVisitor itemVisitor;
|
||||
|
||||
public JsonArrayVisitor(JsonVisitor itemVisitor) {
|
||||
this.itemVisitor = itemVisitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonVisitor array(JsonErrorReporter reporter) {
|
||||
return itemVisitor;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.backend.c.util.json;
|
||||
|
||||
public abstract class JsonConsumer {
|
||||
public void enterObject(JsonErrorReporter reporter) {
|
||||
}
|
||||
|
||||
public void exitObject(JsonErrorReporter reporter) {
|
||||
}
|
||||
|
||||
public void enterArray(JsonErrorReporter reporter) {
|
||||
}
|
||||
|
||||
public void exitArray(JsonErrorReporter reporter) {
|
||||
}
|
||||
|
||||
public void enterProperty(JsonErrorReporter reporter, String name) {
|
||||
}
|
||||
|
||||
public void exitProperty(JsonErrorReporter reporter, String name) {
|
||||
}
|
||||
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
}
|
||||
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
}
|
||||
|
||||
public void floatValue(JsonErrorReporter reporter, double value) {
|
||||
}
|
||||
|
||||
public void nullValue(JsonErrorReporter reporter) {
|
||||
}
|
||||
|
||||
public void booleanValue(JsonErrorReporter reporter, boolean value) {
|
||||
}
|
||||
}
|
|
@ -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.backend.c.util.json;
|
||||
|
||||
public abstract class JsonErrorReporter {
|
||||
public abstract void error(String message);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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.backend.c.util.json;
|
||||
|
||||
public class JsonObjectVisitor extends JsonVisitor {
|
||||
private JsonPropertyVisitor propertyVisitor;
|
||||
|
||||
public JsonObjectVisitor(JsonPropertyVisitor propertyVisitor) {
|
||||
this.propertyVisitor = propertyVisitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonVisitor object(JsonErrorReporter reporter) {
|
||||
return propertyVisitor;
|
||||
}
|
||||
}
|
415
core/src/main/java/org/teavm/backend/c/util/json/JsonParser.java
Normal file
415
core/src/main/java/org/teavm/backend/c/util/json/JsonParser.java
Normal file
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* 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.backend.c.util.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class JsonParser {
|
||||
private JsonConsumer consumer;
|
||||
private int lastChar;
|
||||
private int lineNumber;
|
||||
private int columnNumber;
|
||||
private boolean cr;
|
||||
|
||||
public JsonParser(JsonConsumer consumer) {
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
public void parse(Reader reader) throws IOException {
|
||||
lastChar = reader.read();
|
||||
skipWhitespaces(reader);
|
||||
if (lastChar == -1) {
|
||||
error("Unexpected end of file");
|
||||
}
|
||||
parseValue(reader);
|
||||
skipWhitespaces(reader);
|
||||
if (lastChar != -1) {
|
||||
error("Unexpected characters after end of JSON string");
|
||||
}
|
||||
}
|
||||
|
||||
private void parseValue(Reader reader) throws IOException {
|
||||
switch (lastChar) {
|
||||
case '{':
|
||||
parseObject(reader);
|
||||
break;
|
||||
case '[':
|
||||
parseArray(reader);
|
||||
break;
|
||||
case '\"':
|
||||
parseString(reader);
|
||||
break;
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
parseNumber(reader);
|
||||
break;
|
||||
case 'n':
|
||||
parseNull(reader);
|
||||
break;
|
||||
case 't':
|
||||
parseTrue(reader);
|
||||
break;
|
||||
case 'f':
|
||||
parseFalse(reader);
|
||||
break;
|
||||
default:
|
||||
error("Unexpected character");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void parseObject(Reader reader) throws IOException {
|
||||
Set<String> usedPropertyNames = new HashSet<>();
|
||||
|
||||
consumer.enterObject(errorReporter);
|
||||
nextChar(reader);
|
||||
skipWhitespaces(reader);
|
||||
if (lastChar == '}') {
|
||||
nextChar(reader);
|
||||
consumer.exitObject(errorReporter);
|
||||
return;
|
||||
}
|
||||
|
||||
parseProperty(reader, usedPropertyNames);
|
||||
while (lastChar != '}') {
|
||||
if (lastChar != ',') {
|
||||
error("Either property delimiter (',') or end of object ('}') expected");
|
||||
}
|
||||
nextChar(reader);
|
||||
skipWhitespaces(reader);
|
||||
parseProperty(reader, usedPropertyNames);
|
||||
}
|
||||
nextChar(reader);
|
||||
consumer.exitObject(errorReporter);
|
||||
}
|
||||
|
||||
private void parseProperty(Reader reader, Set<String> usedPropertyNames) throws IOException {
|
||||
skipWhitespaces(reader);
|
||||
if (lastChar != '"') {
|
||||
error("Object key (string literal) expected");
|
||||
}
|
||||
String name = parseStringLiteral(reader);
|
||||
if (!usedPropertyNames.add(name)) {
|
||||
error("Duplicate object property: " + name);
|
||||
}
|
||||
consumer.enterProperty(errorReporter, name);
|
||||
skipWhitespaces(reader);
|
||||
if (lastChar != ':') {
|
||||
error("':' character expected after property name");
|
||||
}
|
||||
nextChar(reader);
|
||||
skipWhitespaces(reader);
|
||||
parseValue(reader);
|
||||
consumer.exitProperty(errorReporter, name);
|
||||
skipWhitespaces(reader);
|
||||
}
|
||||
|
||||
private void parseArray(Reader reader) throws IOException {
|
||||
consumer.enterArray(errorReporter);
|
||||
nextChar(reader);
|
||||
skipWhitespaces(reader);
|
||||
|
||||
if (lastChar == ']') {
|
||||
nextChar(reader);
|
||||
consumer.exitObject(errorReporter);
|
||||
return;
|
||||
}
|
||||
|
||||
parseValue(reader);
|
||||
skipWhitespaces(reader);
|
||||
while (lastChar != ']') {
|
||||
if (lastChar != ',') {
|
||||
error("Either array item delimiter (',') or end of array (']') expected");
|
||||
}
|
||||
nextChar(reader);
|
||||
skipWhitespaces(reader);
|
||||
parseValue(reader);
|
||||
skipWhitespaces(reader);
|
||||
}
|
||||
nextChar(reader);
|
||||
|
||||
consumer.exitArray(errorReporter);
|
||||
}
|
||||
|
||||
private void parseString(Reader reader) throws IOException {
|
||||
consumer.stringValue(errorReporter, parseStringLiteral(reader));
|
||||
}
|
||||
|
||||
private String parseStringLiteral(Reader reader) throws IOException {
|
||||
nextChar(reader);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (lastChar != '\"') {
|
||||
if (lastChar == -1) {
|
||||
error("Unexpected end of input inside string literal");
|
||||
} else if (lastChar < ' ') {
|
||||
error("Unexpected control character inside string literal");
|
||||
}
|
||||
if (lastChar == '\\') {
|
||||
nextChar(reader);
|
||||
switch (lastChar) {
|
||||
case '\"':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(lastChar);
|
||||
nextChar(reader);
|
||||
break;
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
nextChar(reader);
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
nextChar(reader);
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
nextChar(reader);
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
nextChar(reader);
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
nextChar(reader);
|
||||
break;
|
||||
case 'u':
|
||||
nextChar(reader);
|
||||
int code = (getHexDigit(reader) << 12) | (getHexDigit(reader) << 8)
|
||||
| (getHexDigit(reader) << 4) | getHexDigit(reader);
|
||||
sb.append((char) code);
|
||||
break;
|
||||
default:
|
||||
error("Wrong escape sequence");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
sb.append((char) lastChar);
|
||||
nextChar(reader);
|
||||
}
|
||||
}
|
||||
nextChar(reader);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private int getHexDigit(Reader reader) throws IOException {
|
||||
int value;
|
||||
if (lastChar >= '0' && lastChar <= '9') {
|
||||
value = lastChar - '0';
|
||||
} else if (lastChar >= 'A' && lastChar <= 'F') {
|
||||
value = lastChar - 'A' + 10;
|
||||
} else if (lastChar >= 'a' && lastChar <= 'f') {
|
||||
value = lastChar - 'a' + 10;
|
||||
} else {
|
||||
error("Wrong escape sequence");
|
||||
value = 0;
|
||||
}
|
||||
nextChar(reader);
|
||||
return value;
|
||||
}
|
||||
|
||||
private void parseNumber(Reader reader) throws IOException {
|
||||
boolean isFloatingPoint = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (lastChar == '-') {
|
||||
acceptChar(sb, reader);
|
||||
}
|
||||
if (lastChar == '0') {
|
||||
acceptChar(sb, reader);
|
||||
} else {
|
||||
if (!isDigit(lastChar)) {
|
||||
if (lastChar == 'e' || lastChar == 'E' || lastChar == '.') {
|
||||
error("Wrong number, at least one digit expected in integer part");
|
||||
} else {
|
||||
error("Wrong number, digits must follow '-' sign");
|
||||
}
|
||||
}
|
||||
acceptChar(sb, reader);
|
||||
while (isDigit(lastChar)) {
|
||||
acceptChar(sb, reader);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastChar == '.') {
|
||||
isFloatingPoint = true;
|
||||
acceptChar(sb, reader);
|
||||
if (!isDigit(lastChar)) {
|
||||
error("Wrong number, at least one digit must be in fraction part");
|
||||
}
|
||||
acceptChar(sb, reader);
|
||||
while (isDigit(lastChar)) {
|
||||
acceptChar(sb, reader);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastChar == 'e' || lastChar == 'E') {
|
||||
isFloatingPoint = true;
|
||||
acceptChar(sb, reader);
|
||||
if (lastChar == '+' || lastChar == '-') {
|
||||
acceptChar(sb, reader);
|
||||
}
|
||||
if (!isDigit(lastChar)) {
|
||||
error("Wrong number, at least one digit must be in exponent");
|
||||
}
|
||||
acceptChar(sb, reader);
|
||||
while (isDigit(lastChar)) {
|
||||
acceptChar(sb, reader);
|
||||
}
|
||||
}
|
||||
|
||||
expectEndOfToken("Wrong number");
|
||||
|
||||
if (isFloatingPoint) {
|
||||
tryParseDouble(sb);
|
||||
} else {
|
||||
long value;
|
||||
try {
|
||||
value = Long.parseLong(sb.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
tryParseDouble(sb);
|
||||
return;
|
||||
}
|
||||
consumer.intValue(errorReporter, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void tryParseDouble(StringBuilder sb) {
|
||||
double value;
|
||||
try {
|
||||
value = Double.parseDouble(sb.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
error("Wrong number");
|
||||
value = 0;
|
||||
}
|
||||
consumer.floatValue(errorReporter, value);
|
||||
}
|
||||
|
||||
private void acceptChar(StringBuilder sb, Reader reader) throws IOException {
|
||||
sb.append((char) lastChar);
|
||||
nextChar(reader);
|
||||
}
|
||||
|
||||
private void parseNull(Reader reader) throws IOException {
|
||||
expectIdentifier(reader, "null");
|
||||
consumer.nullValue(errorReporter);
|
||||
}
|
||||
|
||||
private void parseTrue(Reader reader) throws IOException {
|
||||
expectIdentifier(reader, "true");
|
||||
consumer.booleanValue(errorReporter, true);
|
||||
}
|
||||
|
||||
private void parseFalse(Reader reader) throws IOException {
|
||||
expectIdentifier(reader, "false");
|
||||
consumer.booleanValue(errorReporter, false);
|
||||
}
|
||||
|
||||
private void expectIdentifier(Reader reader, String identifier) throws IOException {
|
||||
for (int i = 0; i < identifier.length(); ++i) {
|
||||
if (lastChar != identifier.charAt(i)) {
|
||||
error("Unexpected identifier");
|
||||
}
|
||||
nextChar(reader);
|
||||
}
|
||||
expectEndOfToken("Wrong identifier");
|
||||
}
|
||||
|
||||
private void expectEndOfToken(String errorMessage) {
|
||||
switch (lastChar) {
|
||||
case '}':
|
||||
case '{':
|
||||
case '[':
|
||||
case ']':
|
||||
case ',':
|
||||
case ':':
|
||||
break;
|
||||
default:
|
||||
if (!isWhitespace(lastChar)) {
|
||||
error(errorMessage);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void skipWhitespaces(Reader reader) throws IOException {
|
||||
while (isWhitespace(lastChar)) {
|
||||
nextChar(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private void nextChar(Reader reader) throws IOException {
|
||||
boolean wasCr = cr;
|
||||
if (cr) {
|
||||
lineNumber++;
|
||||
columnNumber = 0;
|
||||
cr = false;
|
||||
}
|
||||
switch (lastChar) {
|
||||
case '\r':
|
||||
cr = true;
|
||||
break;
|
||||
case '\n':
|
||||
if (!wasCr) {
|
||||
lineNumber++;
|
||||
columnNumber = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
columnNumber++;
|
||||
break;
|
||||
}
|
||||
lastChar = reader.read();
|
||||
}
|
||||
|
||||
void error(String error) {
|
||||
throw new JsonSyntaxException(lineNumber, columnNumber, error);
|
||||
}
|
||||
|
||||
private JsonErrorReporter errorReporter = new JsonErrorReporter() {
|
||||
@Override
|
||||
public void error(String message) {
|
||||
JsonParser.this.error(message);
|
||||
}
|
||||
};
|
||||
|
||||
private static boolean isWhitespace(int c) {
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDigit(int c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.c.util.json;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class JsonPropertyVisitor extends JsonAllErrorVisitor {
|
||||
private Map<String, JsonVisitor> properties = new HashMap<>();
|
||||
private boolean skipNonExistentProperties;
|
||||
|
||||
public JsonPropertyVisitor(boolean skipNonExistentProperties) {
|
||||
this.skipNonExistentProperties = skipNonExistentProperties;
|
||||
}
|
||||
|
||||
public void addProperty(String propertyName, JsonVisitor visitor) {
|
||||
properties.put(propertyName, visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonVisitor property(JsonErrorReporter reporter, String name) {
|
||||
if (!skipNonExistentProperties && !properties.containsKey(name)) {
|
||||
reporter.error("Unexpected property name: " + name);
|
||||
}
|
||||
return properties.get(name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.c.util.json;
|
||||
|
||||
public class JsonSyntaxException extends RuntimeException {
|
||||
private final int lineNumber;
|
||||
private final int columnNumber;
|
||||
private final String error;
|
||||
|
||||
public JsonSyntaxException(int lineNumber, int columnNumber, String error) {
|
||||
super("JSON syntax error at " + (lineNumber + 1) + ":" + (columnNumber + 1) + ": " + error);
|
||||
this.lineNumber = lineNumber;
|
||||
this.columnNumber = columnNumber;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public int getColumnNumber() {
|
||||
return columnNumber;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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.backend.c.util.json;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
public class JsonVisitingConsumer extends JsonConsumer {
|
||||
private Deque<JsonVisitor> visitorStack = new ArrayDeque<>();
|
||||
private int noVisitorLevel;
|
||||
|
||||
public JsonVisitingConsumer(JsonVisitor visitor) {
|
||||
visitorStack.push(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterObject(JsonErrorReporter reporter) {
|
||||
if (noVisitorLevel == 0) {
|
||||
JsonVisitor next = visitorStack.peek().object(reporter);
|
||||
if (next == null) {
|
||||
noVisitorLevel = 1;
|
||||
} else {
|
||||
visitorStack.push(next);
|
||||
}
|
||||
} else {
|
||||
noVisitorLevel++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitObject(JsonErrorReporter reporter) {
|
||||
exit(reporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterArray(JsonErrorReporter reporter) {
|
||||
if (noVisitorLevel == 0) {
|
||||
JsonVisitor next = visitorStack.peek().array(reporter);
|
||||
if (next == null) {
|
||||
noVisitorLevel = 1;
|
||||
} else {
|
||||
visitorStack.push(next);
|
||||
}
|
||||
} else {
|
||||
noVisitorLevel++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitArray(JsonErrorReporter reporter) {
|
||||
exit(reporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterProperty(JsonErrorReporter reporter, String name) {
|
||||
if (noVisitorLevel == 0) {
|
||||
JsonVisitor next = visitorStack.peek().property(reporter, name);
|
||||
if (next == null) {
|
||||
noVisitorLevel = 1;
|
||||
} else {
|
||||
visitorStack.push(next);
|
||||
}
|
||||
} else {
|
||||
noVisitorLevel++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitProperty(JsonErrorReporter reporter, String name) {
|
||||
exit(reporter);
|
||||
}
|
||||
|
||||
private void exit(JsonErrorReporter reporter) {
|
||||
if (noVisitorLevel > 0) {
|
||||
noVisitorLevel--;
|
||||
} else {
|
||||
visitorStack.pop();
|
||||
}
|
||||
if (noVisitorLevel == 0 && !visitorStack.isEmpty()) {
|
||||
visitorStack.peek().end(reporter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
if (noVisitorLevel == 0) {
|
||||
visitorStack.peek().stringValue(reporter, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
if (noVisitorLevel == 0) {
|
||||
visitorStack.peek().intValue(reporter, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void floatValue(JsonErrorReporter reporter, double value) {
|
||||
if (noVisitorLevel == 0) {
|
||||
visitorStack.peek().floatValue(reporter, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullValue(JsonErrorReporter reporter) {
|
||||
if (noVisitorLevel == 0) {
|
||||
visitorStack.peek().nullValue(reporter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void booleanValue(JsonErrorReporter reporter, boolean value) {
|
||||
if (noVisitorLevel == 0) {
|
||||
visitorStack.peek().booleanValue(reporter, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.backend.c.util.json;
|
||||
|
||||
public abstract class JsonVisitor {
|
||||
public JsonVisitor object(JsonErrorReporter reporter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public JsonVisitor array(JsonErrorReporter reporter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public JsonVisitor property(JsonErrorReporter reporter, String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void stringValue(JsonErrorReporter reporter, String value) {
|
||||
}
|
||||
|
||||
public void intValue(JsonErrorReporter reporter, long value) {
|
||||
}
|
||||
|
||||
public void floatValue(JsonErrorReporter reporter, double value) {
|
||||
}
|
||||
|
||||
public void nullValue(JsonErrorReporter reporter) {
|
||||
}
|
||||
|
||||
public void booleanValue(JsonErrorReporter reporter, boolean value) {
|
||||
}
|
||||
|
||||
public void end(JsonErrorReporter reporter) {
|
||||
}
|
||||
}
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
package org.teavm.runtime;
|
||||
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.interop.c.Name;
|
||||
import org.teavm.interop.c.Native;
|
||||
|
||||
@Unmanaged
|
||||
@StaticInit
|
||||
@Native
|
||||
@Name("TeaVM_CallSite")
|
||||
public class CallSite extends Structure {
|
||||
public ExceptionHandler firstHandler;
|
||||
public CallSiteLocation location;
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
package org.teavm.runtime;
|
||||
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.interop.c.Name;
|
||||
import org.teavm.interop.c.Native;
|
||||
|
||||
@Unmanaged
|
||||
@StaticInit
|
||||
@Native
|
||||
@Name("TeaVM_CallSiteLocation")
|
||||
public class CallSiteLocation extends Structure {
|
||||
public MethodLocation method;
|
||||
public int lineNumber;
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
package org.teavm.runtime;
|
||||
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.interop.c.Name;
|
||||
import org.teavm.interop.c.Native;
|
||||
|
||||
@Unmanaged
|
||||
@StaticInit
|
||||
@Native
|
||||
@Name("TeaVM_ExceptionHandler")
|
||||
public class ExceptionHandler extends Structure {
|
||||
public int id;
|
||||
public RuntimeClass exceptionClass;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.runtime;
|
||||
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.Export;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Structure;
|
||||
|
@ -126,6 +127,14 @@ public final class GC {
|
|||
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
|
||||
}
|
||||
|
||||
@Export(name = "teavm_gc_fixHeap")
|
||||
public static void fixHeap() {
|
||||
if (freeChunks > 0) {
|
||||
currentChunk.classReference = 0;
|
||||
currentChunk.size = (int) (currentChunkLimit.toLong() - currentChunk.toAddress().toLong());
|
||||
}
|
||||
}
|
||||
|
||||
private static void mark() {
|
||||
MemoryTrace.initMark();
|
||||
firstWeakReference = null;
|
||||
|
|
|
@ -40,4 +40,6 @@ public class MemoryTrace {
|
|||
public static native void sweepCompleted();
|
||||
|
||||
public static native void defragCompleted();
|
||||
|
||||
public static native void writeHeapDump();
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
package org.teavm.runtime;
|
||||
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.interop.c.Name;
|
||||
import org.teavm.interop.c.Native;
|
||||
|
||||
@Unmanaged
|
||||
@StaticInit
|
||||
@Native
|
||||
@Name("TeaVM_MethodLocation")
|
||||
public class MethodLocation extends Structure {
|
||||
public StringPtr fileName;
|
||||
public StringPtr className;
|
||||
|
|
|
@ -16,7 +16,11 @@
|
|||
package org.teavm.runtime;
|
||||
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.interop.c.Name;
|
||||
import org.teavm.interop.c.Native;
|
||||
|
||||
@Native
|
||||
@Name("TeaVM_StringPtr")
|
||||
public class StringPtr extends Structure {
|
||||
String value;
|
||||
public String value;
|
||||
}
|
||||
|
|
720
core/src/main/resources/org/teavm/backend/c/heap.c
Normal file
720
core/src/main/resources/org/teavm/backend/c/heap.c
Normal file
|
@ -0,0 +1,720 @@
|
|||
#include "runtime.h"
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#include <uchar.h>
|
||||
|
||||
#ifndef TEAVM_WINDOWS_LOG
|
||||
#define TEAVM_OUTPUT_STRING(s) fprintf(stderr, s)
|
||||
#else
|
||||
#define TEAVM_OUTPUT_STRING(s) OutputDebugStringW(L##s)
|
||||
#endif
|
||||
|
||||
void teavm_outOfMemory() {
|
||||
TEAVM_OUTPUT_STRING("Application crashed due to lack of free memory\n");
|
||||
teavm_gc_writeHeapDump();
|
||||
abort();
|
||||
}
|
||||
|
||||
static wchar_t* teavm_gc_dumpDirectory = NULL;
|
||||
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
void teavm_gc_assertSize(int32_t size) {
|
||||
if (size % sizeof(void*) != 0) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void teavm_gc_allocate(void* address, int32_t size) {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
teavm_gc_assertAddress(address);
|
||||
teavm_gc_assertSize(size);
|
||||
|
||||
size /= sizeof(void*);
|
||||
uint8_t* map = teavm_gc_heapMap + (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
|
||||
if (*map != 0) {
|
||||
fprintf(stderr, "[GC] trying allocate at memory in use at: %d\n",
|
||||
(int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
*map++ = 1;
|
||||
|
||||
for (int32_t i = 1; i < size; ++i) {
|
||||
if (*map != 0) {
|
||||
fprintf(stderr, "[GC] trying allocate at memory in use at: %d\n",
|
||||
(int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
*map++ = 2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_free(void* address, int32_t size) {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
teavm_gc_assertAddress(address);
|
||||
teavm_gc_assertSize(size);
|
||||
|
||||
int32_t offset = (int32_t) (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
uint8_t* markMap = teavm_gc_markMap + offset;
|
||||
size /= sizeof(void*);
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
if (markMap[i] != 0) {
|
||||
fprintf(stderr, "[GC] trying to release reachable object at: %d\n",
|
||||
(int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* map = teavm_gc_heapMap + offset;
|
||||
memset(map, 0, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_assertFree(void* address, int32_t size) {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
teavm_gc_assertAddress(address);
|
||||
teavm_gc_assertSize(size);
|
||||
|
||||
int32_t offset = (int32_t) (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
uint8_t* map = teavm_gc_heapMap + offset;
|
||||
size /= sizeof(void*);
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
if (map[i] != 0) {
|
||||
fprintf(stderr, "[GC] memory supposed to be free at: %d\n",
|
||||
(int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_initMark() {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
memset(teavm_gc_markMap, 0, teavm_gc_availableBytes / sizeof(void*));
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t teavm_gc_objectSize(void* address) {
|
||||
TeaVM_Class* cls = TEAVM_CLASS_OF(address);
|
||||
if (cls->itemType == NULL) {
|
||||
return cls->size;
|
||||
}
|
||||
|
||||
int32_t itemSize = cls->itemType->flags & 2 ? cls->itemType->size : sizeof(void*);
|
||||
TeaVM_Array* array = (TeaVM_Array*) address;
|
||||
char* size = TEAVM_ALIGN((void*) sizeof(TeaVM_Array), itemSize);
|
||||
size += array->size * itemSize;
|
||||
size = TEAVM_ALIGN(size, sizeof(void*));
|
||||
return (int32_t) (intptr_t) size;
|
||||
}
|
||||
|
||||
void teavm_gc_mark(void* address) {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
if (address < teavm_gc_heapAddress
|
||||
|| (char*) address >= (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
teavm_gc_assertAddress(address);
|
||||
|
||||
int32_t offset = (int32_t) (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
uint8_t* map = teavm_gc_heapMap + offset;
|
||||
uint8_t* markMap = teavm_gc_markMap + offset;
|
||||
|
||||
int32_t size = teavm_gc_objectSize(address);
|
||||
teavm_gc_assertSize(size);
|
||||
size /= sizeof(void*);
|
||||
|
||||
if (*map++ != 1 || *markMap != 0) {
|
||||
fprintf(stderr, "[GC] assertion failed marking object at: %d\n", (int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
*markMap++ = 1;
|
||||
|
||||
for (int32_t i = 1; i < size; ++i) {
|
||||
if (*map++ != 2 || *markMap != 0) {
|
||||
abort();
|
||||
}
|
||||
*markMap++ = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_move(void* from, void* to, int32_t size) {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
teavm_gc_assertAddress(from);
|
||||
teavm_gc_assertAddress(to);
|
||||
teavm_gc_assertSize(size);
|
||||
|
||||
uint8_t* mapFrom = teavm_gc_heapMap + (((char*) from - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
uint8_t* mapTo = teavm_gc_heapMap + (((char*) to - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
size /= sizeof(void*);
|
||||
|
||||
if (mapFrom > mapTo) {
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
if (mapFrom[i] == 0 || mapTo[i] != 0) {
|
||||
fprintf(stderr, "[GC] assertion failed moving object from: %d to %d\n",
|
||||
(int) ((char*) from - (char*) teavm_gc_heapAddress), (int) ((char*) to - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
mapTo[i] = mapFrom[i];
|
||||
mapFrom[i] = 0;
|
||||
}
|
||||
} else {
|
||||
for (int32_t i = size - 1; i >= 0; --i) {
|
||||
if (mapFrom[i] == 0 || mapTo[i] != 0) {
|
||||
abort();
|
||||
}
|
||||
mapTo[i] = mapFrom[i];
|
||||
mapFrom[i] = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static FILE* teavm_gc_traceFile = NULL;
|
||||
|
||||
static FILE* teavm_gc_openDumpFile(wchar_t* name) {
|
||||
wchar_t* fullName = name;
|
||||
size_t fullNameLen = wcslen(name);
|
||||
if (teavm_gc_dumpDirectory != NULL) {
|
||||
size_t prefixLen = wcslen(teavm_gc_dumpDirectory);
|
||||
size_t nameLen = fullNameLen;
|
||||
fullNameLen = nameLen + prefixLen;
|
||||
fullName = malloc((prefixLen + nameLen + 1) * sizeof(wchar_t));
|
||||
memcpy(fullName, teavm_gc_dumpDirectory, prefixLen * sizeof(wchar_t));
|
||||
memcpy(fullName + prefixLen, name, (nameLen + 1) * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
FILE* result;
|
||||
#ifdef _MSC_VER
|
||||
_wfopen_s(&result, fullName, L"w");
|
||||
#else
|
||||
size_t fullNameMbSize = 3 * (fullNameLen + 1);
|
||||
char* fullNameMb = malloc(fullNameMbSize);
|
||||
mbstate_t state = { 0 };
|
||||
wcsrtombs(fullNameMb, (const wchar_t **) &fullName, fullNameMbSize, &state);
|
||||
result = fopen(fullNameMb, "w");
|
||||
free(fullNameMb);
|
||||
#endif
|
||||
|
||||
if (fullName != name) {
|
||||
free(fullName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
static void teavm_writeHeapMemory(char* name) {
|
||||
#ifdef TEAVM_GC_LOG
|
||||
if (teavm_gc_traceFile == NULL) {
|
||||
teavm_gc_traceFile = teavm_gc_openDumpFile(L"teavm-gc-trace.txt");
|
||||
}
|
||||
FILE* file = teavm_gc_traceFile;
|
||||
fprintf(file, "%s:", name);
|
||||
|
||||
int32_t numbers = 4096;
|
||||
int64_t mapSize = teavm_gc_availableBytes / sizeof(void*);
|
||||
for (int i = 0; i < numbers; ++i) {
|
||||
int64_t start = mapSize * i / numbers;
|
||||
int64_t end = mapSize * (i + 1) / numbers;
|
||||
int count = 0;
|
||||
for (int j = start; j < end; ++j) {
|
||||
if (teavm_gc_heapMap[j] != 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
int rate = count * 4096 / (end - start);
|
||||
fprintf(file, " %d", rate);
|
||||
}
|
||||
fprintf(file, "\n");
|
||||
fflush(file);
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_checkHeapConsistency() {
|
||||
TeaVM_Object* obj = teavm_gc_heapAddress;
|
||||
while ((char*) obj < (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
|
||||
int32_t size;
|
||||
if (obj->header == 0) {
|
||||
size = obj->hash;
|
||||
teavm_gc_assertFree(obj, size);
|
||||
} else {
|
||||
teavm_verify(obj);
|
||||
TeaVM_Class* cls = TEAVM_CLASS_OF(obj);
|
||||
if (cls->itemType != NULL) {
|
||||
if (!(cls->itemType->flags & 2)) {
|
||||
char* offset = NULL;
|
||||
offset += sizeof(TeaVM_Array);
|
||||
offset = TEAVM_ALIGN(offset, sizeof(void*));
|
||||
void** data = (void**)((char*)obj + (uintptr_t)offset);
|
||||
int32_t size = ((TeaVM_Array*)obj)->size;
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
teavm_verify(data[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (cls != NULL) {
|
||||
int32_t kind = (cls->flags >> 7) & 7;
|
||||
if (kind == 1) {
|
||||
|
||||
} else if (kind == 2) {
|
||||
|
||||
} else {
|
||||
int16_t* layout = cls->layout;
|
||||
if (layout != NULL) {
|
||||
int16_t size = *layout++;
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
void** ptr = (void**) ((char*) obj + *layout++);
|
||||
teavm_verify(*ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cls = cls->superclass;
|
||||
}
|
||||
}
|
||||
size = teavm_gc_objectSize(obj);
|
||||
}
|
||||
obj = (TeaVM_Object*) ((char*) obj + size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void teavm_gc_gcStarted() {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
teavm_writeHeapMemory("start");
|
||||
teavm_gc_checkHeapConsistency();
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_sweepCompleted() {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
teavm_writeHeapMemory("sweep");
|
||||
teavm_gc_checkHeapConsistency();
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_defragCompleted() {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
teavm_writeHeapMemory("defrag");
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_setDumpDirectory(const wchar_t* path) {
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
if (teavm_gc_dumpDirectory != NULL) {
|
||||
free(teavm_gc_dumpDirectory);
|
||||
}
|
||||
size_t pathLen = wcslen(path);
|
||||
size_t bytesLen = sizeof(wchar_t) * (pathLen + 1);
|
||||
teavm_gc_dumpDirectory = malloc(bytesLen);
|
||||
memcpy(teavm_gc_dumpDirectory, path, bytesLen);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TEAVM_HEAP_DUMP
|
||||
static char* teavm_hexChars = "0123456789abcdef";
|
||||
|
||||
static void teavm_gc_escapeJsonString(FILE* out, char16_t* str) {
|
||||
while (1) {
|
||||
char16_t c = (char32_t) *str++;
|
||||
if (c == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '\n':
|
||||
fputc('\\', out);
|
||||
fputc('n', out);
|
||||
break;
|
||||
case '\r':
|
||||
fputc('\\', out);
|
||||
fputc('r', out);
|
||||
break;
|
||||
case '\t':
|
||||
fputc('\\', out);
|
||||
fputc('t', out);
|
||||
break;
|
||||
case '\b':
|
||||
fputc('\\', out);
|
||||
fputc('b', out);
|
||||
break;
|
||||
case '\f':
|
||||
fputc('\\', out);
|
||||
fputc('f', out);
|
||||
break;
|
||||
case '\"':
|
||||
fputc('\\', out);
|
||||
fputc('\"', out);
|
||||
break;
|
||||
case '\\':
|
||||
fputc('\\', out);
|
||||
fputc('\\', out);
|
||||
break;
|
||||
default:
|
||||
if (c < ' ' || c >= 127) {
|
||||
fputc('\\', out);
|
||||
fputc('u', out);
|
||||
fputc(teavm_hexChars[(c >> 12) & 15], out);
|
||||
fputc(teavm_hexChars[(c >> 8) & 15], out);
|
||||
fputc(teavm_hexChars[(c >> 4) & 15], out);
|
||||
fputc(teavm_hexChars[c & 15], out);
|
||||
} else {
|
||||
fputc(c, out);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t teavm_gc_fieldTypeSize(uint8_t type) {
|
||||
switch (type) {
|
||||
case TEAVM_FIELD_TYPE_OBJECT:
|
||||
case TEAVM_FIELD_TYPE_ARRAY:
|
||||
return sizeof(void*);
|
||||
case TEAVM_FIELD_TYPE_BOOLEAN:
|
||||
case TEAVM_FIELD_TYPE_BYTE:
|
||||
return 1;
|
||||
case TEAVM_FIELD_TYPE_CHAR:
|
||||
case TEAVM_FIELD_TYPE_SHORT:
|
||||
return 2;
|
||||
case TEAVM_FIELD_TYPE_INT:
|
||||
case TEAVM_FIELD_TYPE_FLOAT:
|
||||
return 4;
|
||||
case TEAVM_FIELD_TYPE_LONG:
|
||||
case TEAVM_FIELD_TYPE_DOUBLE:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void teavm_gc_writeHeapDumpData(FILE* out, unsigned char* base, uint8_t size) {
|
||||
uint64_t value = *(uint64_t*) base;
|
||||
size *= 2;
|
||||
for (int i = size - 1; i >= 0; --i) {
|
||||
int shift = i * 4;
|
||||
fputc(teavm_hexChars[(value >> shift) & 15], out);
|
||||
}
|
||||
}
|
||||
|
||||
static char* teavm_gc_fieldTypeName(uint8_t type) {
|
||||
switch (type) {
|
||||
case TEAVM_FIELD_TYPE_OBJECT:
|
||||
return "object";
|
||||
case TEAVM_FIELD_TYPE_ARRAY:
|
||||
return "array";
|
||||
case TEAVM_FIELD_TYPE_BOOLEAN:
|
||||
return "boolean";
|
||||
case TEAVM_FIELD_TYPE_BYTE:
|
||||
return "byte";
|
||||
case TEAVM_FIELD_TYPE_CHAR:
|
||||
return "char";
|
||||
case TEAVM_FIELD_TYPE_SHORT:
|
||||
return "short";
|
||||
case TEAVM_FIELD_TYPE_INT:
|
||||
return "int";
|
||||
case TEAVM_FIELD_TYPE_LONG:
|
||||
return "long";
|
||||
case TEAVM_FIELD_TYPE_FLOAT:
|
||||
return "float";
|
||||
case TEAVM_FIELD_TYPE_DOUBLE:
|
||||
return "double";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static char* teavm_gc_primitiveTypeName(int32_t type) {
|
||||
switch (type) {
|
||||
case 0:
|
||||
return "boolean";
|
||||
case 1:
|
||||
return "byte";
|
||||
case 2:
|
||||
return "short";
|
||||
case 3:
|
||||
return "char";
|
||||
case 4:
|
||||
return "int";
|
||||
case 5:
|
||||
return "long";
|
||||
case 6:
|
||||
return "float";
|
||||
case 7:
|
||||
return "double";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void teavm_gc_writeHeapDumpClasses(FILE* out) {
|
||||
fprintf(out, "\"classes\":[");
|
||||
for (int i = 0; i < teavm_classReferencesCount; ++i) {
|
||||
if (i > 0) {
|
||||
fprintf(out, ",\n");
|
||||
}
|
||||
TeaVM_Class* cls = teavm_classReferences[i];
|
||||
fprintf(out, "{\"id\":%" PRIuPTR, (uintptr_t) cls);
|
||||
|
||||
if (cls->name != NULL && !(cls->flags & 2) && cls->itemType == NULL) {
|
||||
fprintf(out, ",\"name\":");
|
||||
char16_t* name = teavm_stringToC16(*cls->name);
|
||||
fprintf(out, "\"");
|
||||
teavm_gc_escapeJsonString(out, name);
|
||||
fprintf(out, "\"");
|
||||
free(name);
|
||||
}
|
||||
|
||||
if (cls->flags & 2) {
|
||||
fprintf(out, ",\"primitive\":\"%s\"", teavm_gc_primitiveTypeName((cls->flags >> 3) & 7));
|
||||
} else if (cls->itemType != NULL) {
|
||||
fprintf(out, ",\"item\":%" PRIuPTR, (uintptr_t) cls->itemType);
|
||||
} else {
|
||||
fprintf(out, ",\"size\":%" PRId32, cls->size);
|
||||
fprintf(out, ",\"super\":");
|
||||
if (cls->superclass != NULL) {
|
||||
fprintf(out, "%" PRIuPTR, (uintptr_t) cls->superclass);
|
||||
} else {
|
||||
fprintf(out, "null");
|
||||
}
|
||||
|
||||
if (cls->fieldDescriptors != NULL) {
|
||||
fprintf(out, ",\"fields\":[");
|
||||
for (int j = 0; j < cls->fieldDescriptors->count; ++j) {
|
||||
if (j > 0) {
|
||||
fprintf(out, ",");
|
||||
}
|
||||
TeaVM_FieldDescriptor* field = &cls->fieldDescriptors->data[j];
|
||||
fprintf(out, "{\"name\":\"");
|
||||
teavm_gc_escapeJsonString(out, field->name);
|
||||
fprintf(out, "\",\"type\":\"%s\"}", teavm_gc_fieldTypeName(field->type));
|
||||
}
|
||||
fprintf(out, "]");
|
||||
}
|
||||
|
||||
if (cls->staticFieldDescriptors != NULL) {
|
||||
fprintf(out, ",\"staticFields\":[");
|
||||
for (int j = 0; j < cls->staticFieldDescriptors->count; ++j) {
|
||||
if (j > 0) {
|
||||
fprintf(out, ",");
|
||||
}
|
||||
TeaVM_StaticFieldDescriptor* field = &cls->staticFieldDescriptors->data[j];
|
||||
fprintf(out, "{\"name\":\"");
|
||||
teavm_gc_escapeJsonString(out, field->name);
|
||||
fprintf(out, "\",\"type\":\"%s\"}", teavm_gc_fieldTypeName(field->type));
|
||||
}
|
||||
fprintf(out, "]");
|
||||
|
||||
fprintf(out, ",\"data\":\"");
|
||||
for (int j = 0; j < cls->staticFieldDescriptors->count; ++j) {
|
||||
TeaVM_StaticFieldDescriptor* field = &cls->staticFieldDescriptors->data[j];
|
||||
teavm_gc_writeHeapDumpData(out, field->offset, teavm_gc_fieldTypeSize(field->type));
|
||||
}
|
||||
fprintf(out, "\"");
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(out, "}");
|
||||
}
|
||||
fprintf(out, "\n]");
|
||||
}
|
||||
|
||||
static int teavm_gc_classDepth(TeaVM_Class* cls) {
|
||||
int depth = 0;
|
||||
while (cls) {
|
||||
depth++;
|
||||
cls = cls->superclass;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
static void teavm_gc_writeHeapDumpObject(FILE* out, TeaVM_Object* obj) {
|
||||
TeaVM_Class* cls = TEAVM_CLASS_OF(obj);
|
||||
fprintf(out, "{\"id\":%" PRIuPTR ",\"class\":%" PRIuPTR, (uintptr_t) obj, (uintptr_t) cls);
|
||||
|
||||
if (cls->itemType != NULL) {
|
||||
int32_t itemSize;
|
||||
if (cls->itemType->flags & 2) {
|
||||
itemSize = cls->itemType->size;
|
||||
} else {
|
||||
itemSize = sizeof(void*);
|
||||
}
|
||||
char* offset = NULL;
|
||||
offset += sizeof(TeaVM_Array);
|
||||
offset = TEAVM_ALIGN(offset, itemSize);
|
||||
unsigned char* data = (unsigned char*) obj + (uintptr_t) offset;
|
||||
int32_t size = ((TeaVM_Array*) obj)->size;
|
||||
|
||||
fprintf(out, ",\"data\":\"");
|
||||
int32_t limit = size * itemSize;
|
||||
for (int32_t i = 0; i < limit; i += itemSize) {
|
||||
teavm_gc_writeHeapDumpData(out, data + i, itemSize);
|
||||
}
|
||||
fprintf(out, "\"");
|
||||
} else {
|
||||
fprintf(out, ",\"data\":\"");
|
||||
int classDepth = teavm_gc_classDepth(cls);
|
||||
TeaVM_Class** classes = malloc(classDepth * sizeof(TeaVM_Class*));
|
||||
int i = classDepth;
|
||||
while (cls != NULL) {
|
||||
classes[--i] = cls;
|
||||
cls = cls->superclass;
|
||||
}
|
||||
for (; i < classDepth; ++i) {
|
||||
cls = classes[i];
|
||||
if (cls->fieldDescriptors != NULL) {
|
||||
TeaVM_FieldDescriptors* fieldDescriptors = cls->fieldDescriptors;
|
||||
for (int j = 0; j < fieldDescriptors->count; ++j) {
|
||||
TeaVM_FieldDescriptor* field = &fieldDescriptors->data[j];
|
||||
teavm_gc_writeHeapDumpData(out, (unsigned char*) obj + field->offset,
|
||||
teavm_gc_fieldTypeSize(field->type));
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(out, "\"");
|
||||
}
|
||||
|
||||
fprintf(out, "}");
|
||||
}
|
||||
|
||||
static void teavm_gc_writeHeapDumpObjects(FILE* out) {
|
||||
fprintf(out, "\"objects\":[");
|
||||
|
||||
int first = 1;
|
||||
TeaVM_Object* obj = teavm_gc_heapAddress;
|
||||
while ((char*) obj < (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
|
||||
int32_t size;
|
||||
if (obj->header == 0) {
|
||||
size = obj->hash;
|
||||
} else {
|
||||
if (!first) {
|
||||
fprintf(out, ",");
|
||||
}
|
||||
first = 0;
|
||||
fprintf(out, "\n");
|
||||
teavm_gc_writeHeapDumpObject(out, obj);
|
||||
size = teavm_gc_objectSize(obj);
|
||||
}
|
||||
obj = (TeaVM_Object*) ((char*) obj + size);
|
||||
}
|
||||
|
||||
TeaVM_HashtableEntrySet* strings = teavm_stringHashtableData;
|
||||
while (strings != NULL) {
|
||||
for (int32_t i = 0; i < strings->size; ++i) {
|
||||
TeaVM_String* str = strings->data[i].data;
|
||||
if ((char*) str >= (char*) teavm_gc_heapAddress
|
||||
&& (char*) str < (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
|
||||
break;
|
||||
}
|
||||
if (!first) {
|
||||
fprintf(out, ",");
|
||||
}
|
||||
first = 0;
|
||||
fprintf(out, "\n");
|
||||
teavm_gc_writeHeapDumpObject(out, (TeaVM_Object*) str);
|
||||
fprintf(out, ",\n");
|
||||
teavm_gc_writeHeapDumpObject(out, (TeaVM_Object*) str->characters);
|
||||
}
|
||||
strings = strings->next;
|
||||
}
|
||||
|
||||
fprintf(out, "\n]");
|
||||
}
|
||||
|
||||
static void teavm_gc_writeHeapDumpStack(FILE* out) {
|
||||
fprintf(out, "\"stack\":[");
|
||||
|
||||
TeaVM_StackFrame* frame = teavm_stackTop;
|
||||
int first = 1;
|
||||
while (frame != NULL) {
|
||||
if (!first) {
|
||||
fprintf(out, ",");
|
||||
}
|
||||
first = 0;
|
||||
fprintf(out, "\n{");
|
||||
|
||||
void** data = &((struct { TeaVM_StackFrame frame; void* data; }*) frame)->data;
|
||||
TeaVM_CallSite* callSite = TEAVM_FIND_CALLSITE(frame->callSiteId, frame);
|
||||
|
||||
if (callSite->location != NULL) {
|
||||
TeaVM_MethodLocation* method = callSite->location->method;
|
||||
if (method != NULL) {
|
||||
if (method->fileName != NULL) {
|
||||
fprintf(out, "\"file\":\"");
|
||||
char16_t* mbName = teavm_stringToC16(*method->fileName);
|
||||
teavm_gc_escapeJsonString(out, mbName);
|
||||
fprintf(out, "\",");
|
||||
free(mbName);
|
||||
}
|
||||
if (method->className != NULL) {
|
||||
fprintf(out, "\"class\":\"");
|
||||
char16_t* mbName = teavm_stringToC16(*method->className);
|
||||
teavm_gc_escapeJsonString(out, mbName);
|
||||
fprintf(out, "\",");
|
||||
free(mbName);
|
||||
}
|
||||
if (method->methodName != NULL) {
|
||||
fprintf(out, "\"method\":\"");
|
||||
char16_t* mbName = teavm_stringToC16(*method->methodName);
|
||||
teavm_gc_escapeJsonString(out, mbName);
|
||||
fprintf(out, "\",");
|
||||
free(mbName);
|
||||
}
|
||||
}
|
||||
if (callSite->location->lineNumber >= 0) {
|
||||
fprintf(out, "\"line\":%" PRId32 ",", callSite->location->lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(out, "\"roots\":[");
|
||||
int rootsFirst = 1;
|
||||
for (int32_t i = 0; i < frame->size; ++i) {
|
||||
if (data[i] != NULL) {
|
||||
if (!rootsFirst) {
|
||||
fprintf(out, ",");
|
||||
}
|
||||
rootsFirst = 0;
|
||||
fprintf(out, "%" PRIuPTR, (uintptr_t) data[i]);
|
||||
}
|
||||
}
|
||||
fprintf(out, "]}");
|
||||
frame = frame->next;
|
||||
}
|
||||
|
||||
fprintf(out, "\n]");
|
||||
}
|
||||
|
||||
static void teavm_gc_writeHeapDumpTo(FILE* out) {
|
||||
fprintf(out, "{\n");
|
||||
fprintf(out, "\"pointerSize\":%u,\n", (unsigned int) sizeof(void*));
|
||||
teavm_gc_writeHeapDumpClasses(out);
|
||||
fprintf(out, ",\n");
|
||||
teavm_gc_writeHeapDumpObjects(out);
|
||||
fprintf(out, ",\n");
|
||||
teavm_gc_writeHeapDumpStack(out);
|
||||
fprintf(out, "\n}");
|
||||
}
|
||||
#endif
|
||||
|
||||
void teavm_gc_writeHeapDump() {
|
||||
#if TEAVM_HEAP_DUMP
|
||||
teavm_gc_fixHeap();
|
||||
FILE* out = teavm_gc_openDumpFile(L"teavm-heap-dump.json");
|
||||
if (out == NULL) {
|
||||
fprintf(stdout, "Error: could not write heap dump");
|
||||
return;
|
||||
}
|
||||
teavm_gc_writeHeapDumpTo(out);
|
||||
fclose(out);
|
||||
#endif
|
||||
}
|
|
@ -254,17 +254,6 @@ void teavm_interrupt() {
|
|||
|
||||
#endif
|
||||
|
||||
#ifndef TEAVM_WINDOWS_LOG
|
||||
#define TEAVM_OUTPUT_STRING(s) fprintf(stderr, s)
|
||||
#else
|
||||
#define TEAVM_OUTPUT_STRING(s) OutputDebugStringW(L##s)
|
||||
#endif
|
||||
|
||||
void teavm_outOfMemory() {
|
||||
TEAVM_OUTPUT_STRING("Application crashed due to lack of free memory\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
static char16_t* teavm_utf16ToUtf32(char16_t* source, char32_t* target) {
|
||||
char16_t c = *source;
|
||||
if ((c & 0xFC00) == 0xD800) {
|
||||
|
@ -426,7 +415,9 @@ char16_t* teavm_stringToC16(void* obj) {
|
|||
char16_t* javaChars = TEAVM_ARRAY_DATA(charArray, char16_t);
|
||||
size_t sz = charArray->size;
|
||||
char16_t* result = malloc((sz + 1) * sizeof(char16_t));
|
||||
memcpy(result, javaChars, sz * sizeof(char16_t));
|
||||
if (sz > 0) {
|
||||
memcpy(result, javaChars, sz * sizeof(char16_t));
|
||||
}
|
||||
result[sz] = 0;
|
||||
return result;
|
||||
}
|
||||
|
@ -598,306 +589,20 @@ void teavm_logchar(int32_t c) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
TeaVM_Class* teavm_classClass;
|
||||
TeaVM_Class* teavm_objectClass;
|
||||
TeaVM_Class* teavm_stringClass;
|
||||
TeaVM_Class* teavm_charArrayClass;
|
||||
|
||||
static wchar_t* teavm_gc_dumpDirectory = NULL;
|
||||
|
||||
void teavm_gc_assertSize(int32_t size) {
|
||||
if (size % sizeof(void*) != 0) {
|
||||
abort();
|
||||
void teavm_initClasses() {
|
||||
teavm_beforeClasses = (char*) teavm_classReferences[0];
|
||||
for (int i = 1; i < teavm_classReferencesCount; ++i) {
|
||||
char* c = (char*) teavm_classReferences[i];
|
||||
if (c < teavm_beforeClasses) teavm_beforeClasses = c;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void teavm_gc_allocate(void* address, int32_t size) {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
teavm_gc_assertAddress(address);
|
||||
teavm_gc_assertSize(size);
|
||||
|
||||
size /= sizeof(void*);
|
||||
uint8_t* map = teavm_gc_heapMap + (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
|
||||
if (*map != 0) {
|
||||
fprintf(stderr, "[GC] trying allocate at memory in use at: %d\n",
|
||||
(int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
*map++ = 1;
|
||||
|
||||
for (int32_t i = 1; i < size; ++i) {
|
||||
if (*map != 0) {
|
||||
fprintf(stderr, "[GC] trying allocate at memory in use at: %d\n",
|
||||
(int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
*map++ = 2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_free(void* address, int32_t size) {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
teavm_gc_assertAddress(address);
|
||||
teavm_gc_assertSize(size);
|
||||
|
||||
int32_t offset = (int32_t) (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
uint8_t* markMap = teavm_gc_markMap + offset;
|
||||
size /= sizeof(void*);
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
if (markMap[i] != 0) {
|
||||
fprintf(stderr, "[GC] trying to release reachable object at: %d\n",
|
||||
(int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* map = teavm_gc_heapMap + offset;
|
||||
memset(map, 0, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_assertFree(void* address, int32_t size) {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
teavm_gc_assertAddress(address);
|
||||
teavm_gc_assertSize(size);
|
||||
|
||||
int32_t offset = (int32_t) (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
uint8_t* map = teavm_gc_heapMap + offset;
|
||||
size /= sizeof(void*);
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
if (map[i] != 0) {
|
||||
fprintf(stderr, "[GC] memory supposed to be free at: %d\n",
|
||||
(int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_initMark() {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
memset(teavm_gc_markMap, 0, teavm_gc_availableBytes / sizeof(void*));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
int32_t teavm_gc_objectSize(void* address) {
|
||||
TeaVM_Class* cls = TEAVM_CLASS_OF(address);
|
||||
if (cls->itemType == NULL) {
|
||||
return cls->size;
|
||||
teavm_beforeClasses -= 4096;
|
||||
int32_t classHeader = TEAVM_PACK_CLASS(teavm_classClass) | (int32_t) INT32_C(0x80000000);
|
||||
for (int i = 0; i < teavm_classReferencesCount; ++i) {
|
||||
teavm_classReferences[i]->parent.header = classHeader;
|
||||
}
|
||||
|
||||
int32_t itemSize = cls->itemType->flags & 2 ? cls->itemType->size : sizeof(void*);
|
||||
TeaVM_Array* array = (TeaVM_Array*) address;
|
||||
char* size = TEAVM_ALIGN((void*) sizeof(TeaVM_Array), itemSize);
|
||||
size += array->size * itemSize;
|
||||
size = TEAVM_ALIGN(size, sizeof(void*));
|
||||
return (int32_t) (intptr_t) size;
|
||||
}
|
||||
#endif
|
||||
|
||||
void teavm_gc_mark(void* address) {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
if (address < teavm_gc_heapAddress
|
||||
|| (char*) address >= (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
teavm_gc_assertAddress(address);
|
||||
|
||||
int32_t offset = (int32_t) (((char*) address - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
uint8_t* map = teavm_gc_heapMap + offset;
|
||||
uint8_t* markMap = teavm_gc_markMap + offset;
|
||||
|
||||
int32_t size = teavm_gc_objectSize(address);
|
||||
teavm_gc_assertSize(size);
|
||||
size /= sizeof(void*);
|
||||
|
||||
if (*map++ != 1 || *markMap != 0) {
|
||||
fprintf(stderr, "[GC] assertion failed marking object at: %d\n", (int) ((char*) address - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
*markMap++ = 1;
|
||||
|
||||
for (int32_t i = 1; i < size; ++i) {
|
||||
if (*map++ != 2 || *markMap != 0) {
|
||||
abort();
|
||||
}
|
||||
*markMap++ = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_move(void* from, void* to, int32_t size) {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
teavm_gc_assertAddress(from);
|
||||
teavm_gc_assertAddress(to);
|
||||
teavm_gc_assertSize(size);
|
||||
|
||||
uint8_t* mapFrom = teavm_gc_heapMap + (((char*) from - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
uint8_t* mapTo = teavm_gc_heapMap + (((char*) to - (char*) teavm_gc_heapAddress) / sizeof(void*));
|
||||
size /= sizeof(void*);
|
||||
|
||||
if (mapFrom > mapTo) {
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
if (mapFrom[i] == 0 || mapTo[i] != 0) {
|
||||
fprintf(stderr, "[GC] assertion failed moving object from: %d to %d\n",
|
||||
(int) ((char*) from - (char*) teavm_gc_heapAddress), (int) ((char*) to - (char*) teavm_gc_heapAddress));
|
||||
abort();
|
||||
}
|
||||
mapTo[i] = mapFrom[i];
|
||||
mapFrom[i] = 0;
|
||||
}
|
||||
} else {
|
||||
for (int32_t i = size - 1; i >= 0; --i) {
|
||||
if (mapFrom[i] == 0 || mapTo[i] != 0) {
|
||||
abort();
|
||||
}
|
||||
mapTo[i] = mapFrom[i];
|
||||
mapFrom[i] = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static FILE* teavm_gc_traceFile = NULL;
|
||||
|
||||
static FILE* teavm_gc_openDumpFile(wchar_t* name) {
|
||||
wchar_t* fullName = name;
|
||||
size_t fullNameLen = wcslen(name);
|
||||
if (teavm_gc_dumpDirectory != NULL) {
|
||||
size_t prefixLen = wcslen(teavm_gc_dumpDirectory);
|
||||
size_t nameLen = fullNameLen;
|
||||
fullNameLen = nameLen + prefixLen;
|
||||
fullName = malloc((prefixLen + nameLen + 1) * sizeof(wchar_t));
|
||||
memcpy(fullName, teavm_gc_dumpDirectory, prefixLen * sizeof(wchar_t));
|
||||
memcpy(fullName + prefixLen, name, (nameLen + 1) * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
FILE* result;
|
||||
#ifdef _MSC_VER
|
||||
_wfopen_s(&result, fullName, L"w");
|
||||
#else
|
||||
size_t fullNameMbSize = 3 * (fullNameLen + 1) * sizeof(wchar_t);
|
||||
char* fullNameMb = malloc(fullNameMbSize);
|
||||
mbstate_t state = { 0 };
|
||||
wcsrtombs(fullNameMb, fullName, fullNameMbSize, state);
|
||||
result = fopen(fullNameMb, "w");
|
||||
#endif
|
||||
|
||||
if (fullName != name) {
|
||||
free(fullName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
static void teavm_writeHeapMemory(char* name) {
|
||||
#ifdef TEAVM_GC_LOG
|
||||
if (teavm_gc_traceFile == NULL) {
|
||||
teavm_gc_traceFile = teavm_gc_openDumpFile(L"teavm-gc-trace.txt");
|
||||
}
|
||||
FILE* file = teavm_gc_traceFile;
|
||||
fprintf(file, "%s:", name);
|
||||
|
||||
int32_t numbers = 4096;
|
||||
int64_t mapSize = teavm_gc_availableBytes / sizeof(void*);
|
||||
for (int i = 0; i < numbers; ++i) {
|
||||
int64_t start = mapSize * i / numbers;
|
||||
int64_t end = mapSize * (i + 1) / numbers;
|
||||
int count = 0;
|
||||
for (int j = start; j < end; ++j) {
|
||||
if (teavm_gc_heapMap[j] != 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
int rate = count * 4096 / (end - start);
|
||||
fprintf(file, " %d", rate);
|
||||
}
|
||||
fprintf(file, "\n");
|
||||
fflush(file);
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_checkHeapConsistency() {
|
||||
TeaVM_Object* obj = teavm_gc_heapAddress;
|
||||
while ((char*) obj < (char*) teavm_gc_heapAddress + teavm_gc_availableBytes) {
|
||||
int32_t size;
|
||||
if (obj->header == 0) {
|
||||
size = obj->hash;
|
||||
teavm_gc_assertFree(obj, size);
|
||||
} else {
|
||||
teavm_verify(obj);
|
||||
TeaVM_Class* cls = TEAVM_CLASS_OF(obj);
|
||||
if (cls->itemType != NULL) {
|
||||
if (!(cls->itemType->flags & 2)) {
|
||||
char* offset = NULL;
|
||||
offset += sizeof(TeaVM_Array);
|
||||
offset = TEAVM_ALIGN(offset, sizeof(void*));
|
||||
void** data = (void**)((char*)obj + (uintptr_t)offset);
|
||||
int32_t size = ((TeaVM_Array*)obj)->size;
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
teavm_verify(data[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (cls != NULL) {
|
||||
int32_t kind = (cls->flags >> 7) & 7;
|
||||
if (kind == 1) {
|
||||
|
||||
} else if (kind == 2) {
|
||||
|
||||
} else {
|
||||
int16_t* layout = cls->layout;
|
||||
if (layout != NULL) {
|
||||
int16_t size = *layout++;
|
||||
for (int32_t i = 0; i < size; ++i) {
|
||||
void** ptr = (void**) ((char*) obj + *layout++);
|
||||
teavm_verify(*ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cls = cls->superclass;
|
||||
}
|
||||
}
|
||||
size = teavm_gc_objectSize(obj);
|
||||
}
|
||||
obj = (TeaVM_Object*) ((char*) obj + size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void teavm_gc_gcStarted() {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
teavm_writeHeapMemory("start");
|
||||
teavm_gc_checkHeapConsistency();
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_sweepCompleted() {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
teavm_writeHeapMemory("sweep");
|
||||
teavm_gc_checkHeapConsistency();
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_defragCompleted() {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
teavm_writeHeapMemory("defrag");
|
||||
#endif
|
||||
}
|
||||
|
||||
void teavm_gc_setDumpDirectory(const wchar_t* path) {
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
if (teavm_gc_dumpDirectory != NULL) {
|
||||
free(teavm_gc_dumpDirectory);
|
||||
}
|
||||
size_t pathLen = wcslen(path);
|
||||
size_t bytesLen = sizeof(wchar_t) * (pathLen + 1);
|
||||
teavm_gc_dumpDirectory = malloc(bytesLen);
|
||||
memcpy(teavm_gc_dumpDirectory, path, bytesLen);
|
||||
#endif
|
||||
}
|
|
@ -27,6 +27,53 @@
|
|||
|
||||
#endif
|
||||
|
||||
#ifndef TEAVM_MEMORY_TRACE
|
||||
#define TEAVM_MEMORY_TRACE 0
|
||||
#endif
|
||||
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
#ifndef TEAVM_HEAP_DUMP
|
||||
#define TEAVM_HEAP_DUMP 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef TEAVM_HEAP_DUMP
|
||||
#define TEAVM_HEAP_DUMP 0
|
||||
#endif
|
||||
|
||||
#define TEAVM_FIELD_TYPE_OBJECT 0
|
||||
#define TEAVM_FIELD_TYPE_ARRAY 1
|
||||
#define TEAVM_FIELD_TYPE_BOOLEAN 2
|
||||
#define TEAVM_FIELD_TYPE_BYTE 3
|
||||
#define TEAVM_FIELD_TYPE_CHAR 4
|
||||
#define TEAVM_FIELD_TYPE_SHORT 5
|
||||
#define TEAVM_FIELD_TYPE_INT 6
|
||||
#define TEAVM_FIELD_TYPE_LONG 7
|
||||
#define TEAVM_FIELD_TYPE_FLOAT 8
|
||||
#define TEAVM_FIELD_TYPE_DOUBLE 9
|
||||
|
||||
typedef struct {
|
||||
uint16_t offset;
|
||||
uint8_t type;
|
||||
char16_t* name;
|
||||
} TeaVM_FieldDescriptor;
|
||||
|
||||
typedef struct {
|
||||
uint32_t count;
|
||||
TeaVM_FieldDescriptor data[65536];
|
||||
} TeaVM_FieldDescriptors;
|
||||
|
||||
typedef struct {
|
||||
unsigned char* offset;
|
||||
uint8_t type;
|
||||
char16_t* name;
|
||||
} TeaVM_StaticFieldDescriptor;
|
||||
|
||||
typedef struct {
|
||||
uint32_t count;
|
||||
TeaVM_StaticFieldDescriptor data[65536];
|
||||
} TeaVM_StaticFieldDescriptors;
|
||||
|
||||
typedef struct TeaVM_Object {
|
||||
int32_t header;
|
||||
int32_t hash;
|
||||
|
@ -54,6 +101,10 @@ typedef struct TeaVM_Class {
|
|||
void* enumValues;
|
||||
void* layout;
|
||||
TeaVM_Object* simpleName;
|
||||
#if TEAVM_HEAP_DUMP
|
||||
TeaVM_FieldDescriptors* fieldDescriptors;
|
||||
TeaVM_StaticFieldDescriptors* staticFieldDescriptors;
|
||||
#endif
|
||||
} TeaVM_Class;
|
||||
|
||||
typedef struct TeaVM_String {
|
||||
|
@ -62,10 +113,52 @@ typedef struct TeaVM_String {
|
|||
int32_t hashCode;
|
||||
} TeaVM_String;
|
||||
|
||||
#define TEAVM_HASHTABLE_ENTRIES 512
|
||||
|
||||
typedef struct TeaVM_HashtableEntry {
|
||||
TeaVM_String* data;
|
||||
int32_t hash;
|
||||
struct TeaVM_HashtableEntry* next;
|
||||
} TeaVM_HashtableEntry;
|
||||
|
||||
typedef struct TeaVM_HashtableEntrySet {
|
||||
TeaVM_HashtableEntry data[TEAVM_HASHTABLE_ENTRIES];
|
||||
int32_t size;
|
||||
struct TeaVM_HashtableEntrySet* next;
|
||||
} TeaVM_HashtableEntrySet;
|
||||
|
||||
extern TeaVM_HashtableEntrySet* teavm_stringHashtableData;
|
||||
|
||||
typedef struct {
|
||||
TeaVM_String* value;
|
||||
} TeaVM_StringPtr;
|
||||
|
||||
typedef struct TeaVM_MethodLocation {
|
||||
TeaVM_String** fileName;
|
||||
TeaVM_String** className;
|
||||
TeaVM_String** methodName;
|
||||
} TeaVM_MethodLocation;
|
||||
|
||||
typedef struct TeaVM_CallSiteLocation {
|
||||
TeaVM_MethodLocation* method;
|
||||
int32_t lineNumber;
|
||||
} TeaVM_CallSiteLocation;
|
||||
|
||||
typedef struct TeaVM_ExceptionHandler {
|
||||
int32_t id;
|
||||
TeaVM_Class* exceptionClass;
|
||||
struct TeaVM_ExceptionHandler* next;
|
||||
} TeaVM_ExceptionHandler;
|
||||
|
||||
typedef struct TeaVM_CallSite {
|
||||
TeaVM_ExceptionHandler* firstHandler;
|
||||
TeaVM_CallSiteLocation* location;
|
||||
} TeaVM_CallSite;
|
||||
|
||||
typedef struct TeaVM_StackFrame {
|
||||
struct TeaVM_StackFrame* next;
|
||||
#ifdef TEAVM_INCREMENTAL
|
||||
void* callSites;
|
||||
TeaVM_CallSite* callSites;
|
||||
#endif
|
||||
#ifdef TEAVM_USE_SETJMP
|
||||
jmp_buf* jmpTarget;
|
||||
|
@ -74,6 +167,13 @@ typedef struct TeaVM_StackFrame {
|
|||
int32_t callSiteId;
|
||||
} TeaVM_StackFrame;
|
||||
|
||||
#ifndef TEAVM_INCREMENTAL
|
||||
extern TeaVM_CallSite teavm_callSites[];
|
||||
#define TEAVM_FIND_CALLSITE(id, frame) (teavm_callSites + id)
|
||||
#else
|
||||
#define TEAVM_FIND_CALLSITE(id, frame) (((TeaVM_StackFrame*) (frame))->callSites + id)
|
||||
#endif
|
||||
|
||||
extern void* teavm_gc_heapAddress;
|
||||
extern void* teavm_gc_gcStorageAddress;
|
||||
extern int32_t teavm_gc_gcStorageSize;
|
||||
|
@ -84,7 +184,7 @@ extern int64_t teavm_gc_availableBytes;
|
|||
extern void*** teavm_gc_staticRoots;
|
||||
extern char* teavm_beforeClasses;
|
||||
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
extern uint8_t* teavm_gc_heapMap;
|
||||
extern uint8_t* teavm_gc_markMap;
|
||||
#endif
|
||||
|
@ -94,7 +194,7 @@ extern char* teavm_beforeClasses;
|
|||
#define TEAVM_CLASS_OF(obj) (TEAVM_UNPACK_CLASS(((TeaVM_Object*) (obj))->header))
|
||||
#define TEAVM_AS(ptr, type) ((type*) (ptr))
|
||||
|
||||
#ifdef TEAVM_MEMORY_TRACE
|
||||
#if TEAVM_MEMORY_TRACE
|
||||
static inline void teavm_gc_assertAddress(void* address) {
|
||||
if ((unsigned int) (uintptr_t) address % sizeof(void*) != 0) {
|
||||
abort();
|
||||
|
@ -474,4 +574,14 @@ extern void teavm_gc_move(void* from, void* to, int32_t size);
|
|||
extern void teavm_gc_gcStarted();
|
||||
extern void teavm_gc_sweepCompleted();
|
||||
extern void teavm_gc_defragCompleted();
|
||||
extern void teavm_gc_setDumpDirectory(const wchar_t* path);
|
||||
extern void teavm_gc_setDumpDirectory(const wchar_t* path);
|
||||
extern void teavm_gc_fixHeap();
|
||||
extern void teavm_gc_writeHeapDump();
|
||||
|
||||
extern TeaVM_Class* teavm_classReferences[];
|
||||
extern TeaVM_Class* teavm_classClass;
|
||||
extern TeaVM_Class* teavm_objectClass;
|
||||
extern TeaVM_Class* teavm_stringClass;
|
||||
extern TeaVM_Class* teavm_charArrayClass;
|
||||
extern int32_t teavm_classReferencesCount;
|
||||
extern void teavm_initClasses();
|
|
@ -5,22 +5,8 @@
|
|||
#include <uchar.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#define TEAVM_HASHTABLE_ENTRIES 512
|
||||
|
||||
typedef struct TeaVM_HashtableEntry {
|
||||
TeaVM_String* data;
|
||||
int32_t hash;
|
||||
struct TeaVM_HashtableEntry* next;
|
||||
} TeaVM_HashtableEntry;
|
||||
|
||||
typedef struct TeaVM_HashtableEntrySet {
|
||||
TeaVM_HashtableEntry data[TEAVM_HASHTABLE_ENTRIES];
|
||||
int32_t size;
|
||||
struct TeaVM_HashtableEntrySet* next;
|
||||
} TeaVM_HashtableEntrySet;
|
||||
|
||||
static TeaVM_HashtableEntry** teavm_stringHashtable = NULL;
|
||||
static TeaVM_HashtableEntrySet* teavm_stringHashtableData = NULL;
|
||||
TeaVM_HashtableEntrySet* teavm_stringHashtableData = NULL;
|
||||
static int32_t teavm_stringHashtableSize = 0;
|
||||
static int32_t teavm_stringHashtableFill = 0;
|
||||
static int32_t teavm_stringHashtableThreshold = 0;
|
||||
|
@ -80,6 +66,9 @@ static void teavm_rehashStrings() {
|
|||
}
|
||||
|
||||
TeaVM_String* teavm_registerString(TeaVM_String* str) {
|
||||
str->parent.header = TEAVM_PACK_CLASS(teavm_stringClass);
|
||||
str->characters->parent.header = TEAVM_PACK_CLASS(teavm_charArrayClass);
|
||||
|
||||
if (teavm_stringHashtable == NULL) {
|
||||
teavm_stringHashtableSize = 256;
|
||||
teavm_updateStringHashtableThreshold();
|
||||
|
|
|
@ -333,6 +333,7 @@ public class IncrementalCBuilder {
|
|||
cTarget.setMinHeapSize(minHeapSize * 1024 * 1024);
|
||||
cTarget.setLineNumbersGenerated(lineNumbersGenerated);
|
||||
cTarget.setLongjmpUsed(longjmpSupported);
|
||||
cTarget.setHeapDump(true);
|
||||
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
|
||||
vm.setCacheStatus(classSource);
|
||||
vm.addVirtualMethods(m -> true);
|
||||
|
|
|
@ -322,6 +322,9 @@ public final class TeaVMRunner {
|
|||
if (commandLine.hasOption("no-longjmp")) {
|
||||
tool.setLongjmpSupported(false);
|
||||
}
|
||||
if (commandLine.hasOption("heap-dump")) {
|
||||
tool.setHeapDump(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseHeap() {
|
||||
|
|
|
@ -104,6 +104,7 @@ public class TeaVMTool {
|
|||
private int minHeapSize = 32 * (1 << 20);
|
||||
private ReferenceCache referenceCache;
|
||||
private boolean longjmpSupported = true;
|
||||
private boolean heapDump;
|
||||
|
||||
public File getTargetDirectory() {
|
||||
return targetDirectory;
|
||||
|
@ -253,6 +254,10 @@ public class TeaVMTool {
|
|||
this.longjmpSupported = longjmpSupported;
|
||||
}
|
||||
|
||||
public void setHeapDump(boolean heapDump) {
|
||||
this.heapDump = heapDump;
|
||||
}
|
||||
|
||||
public void setProgressListener(TeaVMProgressListener progressListener) {
|
||||
this.progressListener = progressListener;
|
||||
}
|
||||
|
@ -328,6 +333,7 @@ public class TeaVMTool {
|
|||
cTarget.setMinHeapSize(minHeapSize);
|
||||
cTarget.setLineNumbersGenerated(debugInformationGenerated);
|
||||
cTarget.setLongjmpUsed(longjmpSupported);
|
||||
cTarget.setHeapDump(heapDump);
|
||||
return cTarget;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,5 +76,7 @@ public interface BuildStrategy {
|
|||
|
||||
void setLongjmpSupported(boolean value);
|
||||
|
||||
void setHeapDump(boolean heapDump);
|
||||
|
||||
BuildResult build() throws BuildException;
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
|||
private int heapSize = 32;
|
||||
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
|
||||
private boolean longjmpSupported = true;
|
||||
private boolean heapDump;
|
||||
private TeaVMProgressListener progressListener;
|
||||
private Properties properties = new Properties();
|
||||
private TeaVMToolLog log = new EmptyTeaVMToolLog();
|
||||
|
@ -202,6 +203,11 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
|||
this.longjmpSupported = longjmpSupported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeapDump(boolean heapDump) {
|
||||
this.heapDump = heapDump;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildResult build() throws BuildException {
|
||||
TeaVMTool tool = new TeaVMTool();
|
||||
|
@ -229,6 +235,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
|||
tool.setWasmVersion(wasmVersion);
|
||||
tool.setMinHeapSize(heapSize);
|
||||
tool.setLongjmpSupported(longjmpSupported);
|
||||
tool.setHeapDump(heapDump);
|
||||
|
||||
tool.getProperties().putAll(properties);
|
||||
|
||||
|
|
|
@ -180,6 +180,11 @@ public class RemoteBuildStrategy implements BuildStrategy {
|
|||
request.longjmpSupported = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeapDump(boolean heapDump) {
|
||||
request.heapDump = heapDump;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildResult build() throws BuildException {
|
||||
RemoteBuildResponse response;
|
||||
|
|
|
@ -160,6 +160,7 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
|
|||
tool.setWasmVersion(request.wasmVersion);
|
||||
tool.setMinHeapSize(request.heapSize);
|
||||
tool.setLongjmpSupported(request.longjmpSupported);
|
||||
tool.setHeapDump(request.heapDump);
|
||||
|
||||
for (String sourceDirectory : request.sourceDirectories) {
|
||||
tool.addSourceFileProvider(new DirectorySourceFileProvider(new File(sourceDirectory)));
|
||||
|
|
|
@ -47,4 +47,5 @@ public class RemoteBuildRequest implements Serializable {
|
|||
public WasmBinaryVersion wasmVersion;
|
||||
public int heapSize;
|
||||
public boolean longjmpSupported;
|
||||
public boolean heapDump;
|
||||
}
|
||||
|
|
|
@ -149,6 +149,9 @@ public class TeaVMCompileMojo extends AbstractMojo {
|
|||
@Parameter(property = "teavm.longjmpSupported", defaultValue = "true")
|
||||
private boolean longjmpSupported;
|
||||
|
||||
@Parameter(property = "teavm.heapDump", defaultValue = "false")
|
||||
private boolean heapDump;
|
||||
|
||||
private void setupBuilder(BuildStrategy builder) throws MojoExecutionException {
|
||||
builder.setLog(new MavenTeaVMToolLog(getLog()));
|
||||
try {
|
||||
|
@ -281,6 +284,7 @@ public class TeaVMCompileMojo extends AbstractMojo {
|
|||
builder.setTargetType(targetType);
|
||||
builder.setWasmVersion(wasmVersion);
|
||||
builder.setLongjmpSupported(longjmpSupported);
|
||||
builder.setHeapDump(heapDump);
|
||||
BuildResult result;
|
||||
result = builder.build();
|
||||
TeaVMProblemRenderer.describeProblems(result.getCallGraph(), result.getProblems(), toolLog);
|
||||
|
|
Loading…
Reference in New Issue
Block a user