diff --git a/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java index 3c0c6ae0a..84e41f167 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java @@ -34,7 +34,8 @@ public class StringPoolGenerator { if (codes) { generateNumericStringLiteral(s); } else { - generateSimpleStringLiteral(s); + writer.print("u"); + generateSimpleStringLiteral(writer, s); } writer.print(")"); @@ -55,9 +56,9 @@ public class StringPoolGenerator { return false; } - private void generateSimpleStringLiteral(String string) { + public static void generateSimpleStringLiteral(CodeWriter writer, String string) { if (string.isEmpty()) { - writer.print("u\"\""); + writer.print("\"\""); return; } @@ -67,7 +68,7 @@ public class StringPoolGenerator { writer.println(); } int last = Math.min(i + chunkSize, string.length()); - writer.print("u\""); + writer.print("\""); for (int j = i; j < last; ++j) { char c = string.charAt(j); diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/StringsIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/StringsIntrinsic.java index 2fc862f4d..ae2bae58f 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/StringsIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/StringsIntrinsic.java @@ -15,7 +15,10 @@ */ package org.teavm.backend.c.intrinsic; +import org.teavm.ast.ConstantExpr; +import org.teavm.ast.Expr; import org.teavm.ast.InvocationExpr; +import org.teavm.backend.c.generate.StringPoolGenerator; import org.teavm.interop.Strings; import org.teavm.model.MethodReference; @@ -28,11 +31,18 @@ public class StringsIntrinsic implements Intrinsic { @Override public void apply(IntrinsicContext context, InvocationExpr invocation) { switch (invocation.getMethod().getName()) { - case "toC": - context.writer().print("teavm_stringToC("); - context.emit(invocation.getArguments().get(0)); - context.writer().print(")"); + case "toC": { + Expr arg = invocation.getArguments().get(0); + String literal = extractStringConstant(arg); + if (literal != null) { + StringPoolGenerator.generateSimpleStringLiteral(context.writer(), literal); + } else { + context.writer().print("teavm_stringToC("); + context.emit(arg); + context.writer().print(")"); + } break; + } case "fromC": context.writer().print("teavm_cToString("); context.emit(invocation.getArguments().get(0)); @@ -40,4 +50,13 @@ public class StringsIntrinsic implements Intrinsic { break; } } + + private String extractStringConstant(Expr expr) { + if (!(expr instanceof ConstantExpr)) { + return null; + } + + Object value = ((ConstantExpr) expr).getValue(); + return value instanceof String ? (String) value : null; + } } diff --git a/core/src/main/java/org/teavm/runtime/Console.java b/core/src/main/java/org/teavm/runtime/Console.java new file mode 100644 index 000000000..9be461b23 --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/Console.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.runtime; + +import org.teavm.interop.Address; +import org.teavm.interop.Import; +import org.teavm.interop.StaticInit; +import org.teavm.interop.Unmanaged; + +@Unmanaged +@StaticInit +public final class Console { + private Console() { + } + + @Import(name = "teavm_printString") + public static native void printString(Address ptr); + + @Import(name = "teavm_printInt") + public static native void printInt(int n); +} diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java index 88bef8522..1ff1d6f87 100644 --- a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java @@ -18,6 +18,7 @@ package org.teavm.runtime; import org.teavm.interop.Address; import org.teavm.interop.Export; import org.teavm.interop.StaticInit; +import org.teavm.interop.Strings; import org.teavm.interop.Structure; import org.teavm.interop.Unmanaged; @@ -28,6 +29,32 @@ public final class ExceptionHandling { public static native CallSite findCallSiteById(int id); + public static void printStack() { + Address stackFrame = ShadowStack.getNextStackFrame(ShadowStack.getStackTop()); + while (stackFrame != null) { + int callSiteId = ShadowStack.getCallSiteId(stackFrame); + CallSite callSite = findCallSiteById(callSiteId); + CallSiteLocation location = callSite.location; + + Console.printString(Strings.toC(" at ")); + if (location.className == null || location.methodName == null) { + Console.printString(Strings.toC("(Unknown method)")); + } else { + Console.printString(Strings.toC(location.className)); + Console.printString(Strings.toC(".")); + Console.printString(Strings.toC(location.methodName)); + } + Console.printString(Strings.toC("(")); + if (location.fileName != null && location.lineNumber >= 0) { + Console.printString(Strings.toC(location.fileName)); + Console.printString(Strings.toC(":")); + Console.printInt(location.lineNumber); + } + Console.printString(Strings.toC(")\n")); + stackFrame = ShadowStack.getNextStackFrame(stackFrame); + } + } + private static Throwable thrownException; @Export(name = "sys_catchException") diff --git a/core/src/main/java/org/teavm/runtime/GC.java b/core/src/main/java/org/teavm/runtime/GC.java index 121fecc85..ae11b240b 100644 --- a/core/src/main/java/org/teavm/runtime/GC.java +++ b/core/src/main/java/org/teavm/runtime/GC.java @@ -16,6 +16,7 @@ package org.teavm.runtime; import org.teavm.interop.Address; +import org.teavm.interop.Import; import org.teavm.interop.StaticInit; import org.teavm.interop.Structure; import org.teavm.interop.Unmanaged; @@ -46,6 +47,9 @@ public final class GC { private static native int regionSize(); + @Import(name = "teavm_outOfMemory") + private static native void outOfMemory(); + public static int getFreeMemory() { return freeMemory; } @@ -88,7 +92,10 @@ public final class GC { return; } collectGarbage(size); - getAvailableChunkIfPossible(size); + if (!getAvailableChunkIfPossible(size)) { + ExceptionHandling.printStack(); + outOfMemory(); + } } private static boolean getAvailableChunkIfPossible(int size) { diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.c b/core/src/main/resources/org/teavm/backend/c/runtime.c index 9f3321980..db0e9606e 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.c +++ b/core/src/main/resources/org/teavm/backend/c/runtime.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -351,4 +352,17 @@ static void teavm_interrupt() { raise(SIGRTMIN); } -#endif \ No newline at end of file +#endif + +static void teavm_outOfMemory() { + fprintf(stderr, "Application crashed due to lack of free memory\n"); + exit(1); +} + +static void teavm_printString(char* s) { + fprintf(stderr, "%s", s); +} + +static void teavm_printInt(int32_t i) { + fprintf(stderr, "%" PRId32, i); +} \ No newline at end of file