From 37286456654592b8b452a1575b808c2d55b5a56f Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 1 Feb 2022 08:46:35 +0300 Subject: [PATCH] Add Kotlin hack to avoid using regexp in String.toFloatOrNull and String.toDoubleOrNull --- .../org/teavm/classlib/impl/KotlinHacks.java | 96 ++++++++++++++----- 1 file changed, 70 insertions(+), 26 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/impl/KotlinHacks.java b/classlib/src/main/java/org/teavm/classlib/impl/KotlinHacks.java index b506a661f..e8d7916f7 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/KotlinHacks.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/KotlinHacks.java @@ -22,50 +22,94 @@ import org.teavm.model.BasicBlock; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformerContext; +import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReader; import org.teavm.model.Program; import org.teavm.model.instructions.ExitInstruction; import org.teavm.model.instructions.StringConstantInstruction; +import org.teavm.model.util.ProgramUtils; public class KotlinHacks implements ClassHolderTransformer { @Override public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) { if (cls.getName().equals("kotlin.jvm.internal.Lambda") || cls.getName().equals("kotlin.coroutines.jvm.internal.BaseContinuationImpl")) { - List methodsToRemove = new ArrayList<>(); - for (MethodHolder method : cls.getMethods()) { - if (method.getName().equals("toString")) { - methodsToRemove.add(method); - } - } - for (MethodHolder method : methodsToRemove) { - cls.removeMethod(method); - } + patchContinuation(cls); } else if (cls.getName().equals("kotlin.jvm.internal.Reflection")) { - for (MethodHolder method : cls.getMethods()) { - if (method.getLevel() == AccessLevel.PUBLIC && method.hasModifier(ElementModifier.STATIC) - && method.getName().equals("renderLambdaToString") - && method.getResultType().isObject(String.class)) { - Program program = new Program(); + patchReflection(cls); + } else if (cls.getName().equals("kotlin.text.StringsKt__StringNumberConversionsJVMKt")) { + patchStrings(cls, context.getHierarchy().getClassSource()); + } + } + + private void patchContinuation(ClassHolder cls) { + List methodsToRemove = new ArrayList<>(); + for (MethodHolder method : cls.getMethods()) { + if (method.getName().equals("toString")) { + methodsToRemove.add(method); + } + } + for (MethodHolder method : methodsToRemove) { + cls.removeMethod(method); + } + } + + private void patchReflection(ClassHolder cls) { + for (MethodHolder method : cls.getMethods()) { + if (method.getLevel() == AccessLevel.PUBLIC && method.hasModifier(ElementModifier.STATIC) + && method.getName().equals("renderLambdaToString") + && method.getResultType().isObject(String.class)) { + Program program = new Program(); + program.createVariable(); + for (int i = 0; i < method.parameterCount(); ++i) { program.createVariable(); - for (int i = 0; i < method.parameterCount(); ++i) { - program.createVariable(); - } - BasicBlock block = program.createBasicBlock(); + } + BasicBlock block = program.createBasicBlock(); - StringConstantInstruction stringConstant = new StringConstantInstruction(); - stringConstant.setReceiver(program.createVariable()); - stringConstant.setConstant("lambda"); - block.add(stringConstant); + StringConstantInstruction stringConstant = new StringConstantInstruction(); + stringConstant.setReceiver(program.createVariable()); + stringConstant.setConstant("lambda"); + block.add(stringConstant); - ExitInstruction exit = new ExitInstruction(); - exit.setValueToReturn(stringConstant.getReceiver()); - block.add(exit); + ExitInstruction exit = new ExitInstruction(); + exit.setValueToReturn(stringConstant.getReceiver()); + block.add(exit); - method.setProgram(program); + method.setProgram(program); + } + } + } + + private void patchStrings(ClassHolder cls, ClassReaderSource source) { + ClassReader templateClass = source.get(KotlinStrings.class.getName()); + for (MethodHolder method : cls.getMethods()) { + if (method.getName().equals("toDoubleOrNull") || method.getName().equals("toFloatOrNull")) { + MethodReader templateMethod = templateClass.getMethod(method.getDescriptor()); + if (templateMethod != null) { + method.setProgram(ProgramUtils.copy(templateMethod.getProgram())); } } } } + + static class KotlinStrings { + static Float toFloatOrNull(String value) { + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + return null; + } + } + + static Double toDoubleOrNull(String value) { + try { + return Double.parseDouble(value); + } catch (NumberFormatException e) { + return null; + } + } + } }