diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 67fdbcff6..01cc3f549 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -130,7 +130,7 @@ public class ClassGenerator { generateStaticGCRoots(); generateLayoutArray(); - new StringPoolGenerator(stringPoolWriter, context.getNames()).generate(context.getStringPool().getStrings()); + new StringPoolGenerator(stringPoolWriter).generate(context.getStringPool().getStrings()); for (String include : includes) { includesWriter.println("#include " + include); diff --git a/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java b/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java index e98251c71..0893982ac 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java +++ b/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java @@ -69,6 +69,8 @@ public class NameProvider { memberFieldNames.put(new FieldReference(RuntimeObject.class.getName(), "classReference"), "header"); memberFieldNames.put(new FieldReference(RuntimeArray.class.getName(), "size"), "size"); + memberFieldNames.put(new FieldReference(String.class.getName(), "characters"), "characters"); + memberFieldNames.put(new FieldReference(String.class.getName(), "hashCode"), "hashCode"); occupiedClassNames.put(RuntimeObject.class.getName(), new HashSet<>(Arrays.asList("header"))); occupiedClassNames.put(RuntimeArray.class.getName(), new HashSet<>(Arrays.asList("length"))); 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 e141a6ba6..58637aebd 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 @@ -16,56 +16,46 @@ package org.teavm.backend.c.generate; import java.util.List; -import org.teavm.model.FieldReference; public class StringPoolGenerator { private CodeWriter writer; - private NameProvider names; - public StringPoolGenerator(CodeWriter writer, NameProvider names) { + public StringPoolGenerator(CodeWriter writer) { this.writer = writer; - this.names = names; } public void generate(List strings) { - generateStringArrays(strings); - generateStringObjects(strings); - } - - private void generateStringArrays(List strings) { - for (int i = 0; i < strings.size(); ++i) { - String s = strings.get(i); - writer.print("static struct { JavaArray hdr; char16_t data[" + (s.length() + 1) + "]; } str_array_" + i) - .println(" = {").indent(); - writer.println(".hdr = { .size = " + s.length() + "},"); - writer.print(".data = "); - generateStringLiteral(s); - writer.println(); - - writer.outdent().println("};"); - } - } - - private void generateStringObjects(List strings) { - String charactersName = names.forMemberField(new FieldReference(String.class.getName(), "characters")); - String hashCodeName = names.forMemberField(new FieldReference(String.class.getName(), "hashCode")); - writer.println("static JavaString stringPool[" + strings.size() + "] = {").indent(); for (int i = 0; i < strings.size(); ++i) { - writer.println("{").indent(); - writer.println("." + charactersName + " = (JavaArray*) &str_array_" + i + ","); - writer.println("." + hashCodeName + " = INT32_C(" + strings.get(i).hashCode() + ")"); - writer.outdent().print("}"); - - if (i < strings.size() - 1) { - writer.print(","); + String s = strings.get(i); + boolean codes = hasBadCharacters(s); + String macroName = codes ? "TEAVM_STRING_FROM_CODES" : "TEAVM_STRING"; + writer.print(macroName + "(" + s.length() + ", " + s.hashCode() + ","); + if (codes) { + generateNumericStringLiteral(s); + } else { + generateSimpleStringLiteral(s); } + writer.print(")"); + + writer.print(i < strings.size() - 1 ? "," : " "); + writer.print(" // string #" + i); writer.println(); } writer.outdent().println("};"); } - private void generateStringLiteral(String string) { + private boolean hasBadCharacters(String string) { + for (int i = 0; i < string.length(); ++i) { + char c = string.charAt(i); + if (c == 0 || Character.isSurrogate(c)) { + return true; + } + } + return false; + } + + private void generateSimpleStringLiteral(String string) { writer.print("u\""); for (int j = 0; j < string.length(); ++j) { @@ -88,7 +78,7 @@ public class StringPoolGenerator { break; default: if (c < 32) { - writer.print("\\x" + Character.forDigit(c >> 4, 16) + Character.forDigit(c & 0xF, 16)); + writer.print("\\0" + Character.forDigit(c >> 3, 8) + Character.forDigit(c & 0x7, 8)); } else if (c > 127) { writer.print("\\u" + Character.forDigit(c >> 12, 16) @@ -104,4 +94,14 @@ public class StringPoolGenerator { writer.print("\""); } + + private void generateNumericStringLiteral(String string) { + for (int i = 0; i < string.length(); ++i) { + if (i > 0) { + writer.print(", "); + } + int c = string.charAt(i); + writer.print(Integer.toString(c)); + } + } } 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 9c0721313..47c91794e 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.c +++ b/core/src/main/resources/org/teavm/backend/c/runtime.c @@ -80,6 +80,22 @@ static inline void* checkcast(void*, int32_t (*)(JavaClass*)); #define ADDRESS_ADD(address, offset) ((char *) (address) + (offset)) #define STRUCTURE_ADD(structure, address, offset) (((structure*) (address)) + offset) +#define TEAVM_STRING(length, hash, s) { \ + .characters = (JavaArray*) & (struct { JavaArray hdr; char16_t data[length]; }) { \ + .hdr = { .size = length }, \ + .data = s \ + }, \ + .hashCode = INT32_C(hash) \ +} + +#define TEAVM_STRING_FROM_CODES(length, hash, ...) { \ + .characters = (JavaArray*) & (struct { JavaArray hdr; char16_t data[length]; }) { \ + .hdr = { .size = length }, \ + .data = { __VA_ARGS__ } \ + }, \ + .hashCode = INT32_C(hash) \ +} + static void** stackTop; static void* gc_gcStorageAddress = NULL;