From 7f875aa56808443fcf2af92d15049834398107fa Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 21 May 2019 15:17:58 +0300 Subject: [PATCH] C: support methods of Date class --- .../reflect/AnnotationDependencyListener.java | 7 - .../org/teavm/classlib/java/util/TDate.java | 150 ++++++++++++++++- .../java/org/teavm/backend/c/CTarget.java | 6 +- .../backend/c/generate/ClassGenerator.java | 19 +-- .../main/resources/org/teavm/backend/c/date.c | 152 ++++++++++++++++++ .../resources/org/teavm/backend/c/runtime.h | 21 ++- 6 files changed, 325 insertions(+), 30 deletions(-) create mode 100644 core/src/main/resources/org/teavm/backend/c/date.c diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java index 6a31cc4b9..b4b1b971e 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java @@ -18,9 +18,7 @@ package org.teavm.classlib.java.lang.reflect; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.teavm.dependency.AbstractDependencyListener; import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyNode; @@ -44,7 +42,6 @@ import org.teavm.platform.PlatformAnnotationProvider; import org.teavm.platform.PlatformClass; public class AnnotationDependencyListener extends AbstractDependencyListener { - private Set reachedMethods = new HashSet<>(); private static final MethodReference GET_ANNOTATIONS_METHOD = new MethodReference( Platform.class, "getAnnotations", PlatformClass.class, Annotation[].class); private static final String ANNOTATIONS_READER_SUFFIX = "$$__annotations__$$"; @@ -120,10 +117,6 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { @Override public void methodReached(DependencyAgent agent, MethodDependency method) { - if (!reachedMethods.add(method.getReference())) { - return; - } - ValueType type = method.getMethod().getResultType(); while (type instanceof ValueType.Array) { type = ((ValueType.Array) type).getItemType(); diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java b/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java index 42dabe755..7e1146d7e 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java @@ -19,11 +19,23 @@ import java.util.TimeZone; import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.java.lang.TComparable; import org.teavm.classlib.java.lang.TSystem; +import org.teavm.interop.Import; +import org.teavm.interop.NoSideEffects; import org.teavm.jso.core.JSDate; public class TDate implements TComparable { private long value; + static { + if (PlatformDetector.isLowLevel()) { + initLowLevel(); + } + } + + @Import(name = "teavm_date_init") + @NoSideEffects + private static native void initLowLevel(); + public TDate() { value = TSystem.currentTimeMillis(); } @@ -44,10 +56,18 @@ public class TDate implements TComparable { @Deprecated public TDate(int year, int month, int date, int hrs, int min, int sec) { - this((long) JSDate.create(year, month, date, hrs, min, sec).getTime()); - setYear(year); + this(PlatformDetector.isLowLevel() + ? initDateLowLevel(year, month, date, hrs, min, sec) + : (long) JSDate.create(year, month, date, hrs, min, sec).getTime()); + if (!PlatformDetector.isLowLevel()) { + setYear(year); + } } + @Import(name = "teavm_date_create") + @NoSideEffects + private static native long initDateLowLevel(int year, int month, int date, int hrs, int min, int sec); + public TDate(String s) { this(parse(s)); } @@ -59,11 +79,23 @@ public class TDate implements TComparable { @Deprecated public static long UTC(int year, int month, int date, int hrs, int min, int sec) { - return (long) JSDate.UTC(year, month, date, hrs, min, sec); + if (PlatformDetector.isLowLevel()) { + return initUtcDateLowLevel(year, month, date, hrs, min, sec); + } else { + return (long) JSDate.UTC(year, month, date, hrs, min, sec); + } } + @Import(name = "teavm_date_createUtc") + @NoSideEffects + private static native long initUtcDateLowLevel(int year, int month, int date, int hrs, int min, int sec); + @Deprecated public static long parse(String s) { + if (PlatformDetector.isLowLevel()) { + return parseLowLevel(s); + } + double value = JSDate.parse(s); if (Double.isNaN(value)) { throw new IllegalArgumentException("Can't parse date: " + s); @@ -71,83 +103,183 @@ public class TDate implements TComparable { return (long) value; } + @Import(name = "teavm_date_parse") + @NoSideEffects + private static native long parseLowLevel(String s); + @Deprecated public int getYear() { + if (PlatformDetector.isLowLevel()) { + return getYearLowLevel(value); + } return JSDate.create(value).getFullYear() - 1900; } + @Import(name = "teavm_date_getYear") + @NoSideEffects + private static native int getYearLowLevel(long date); + @Deprecated public void setYear(int year) { + if (PlatformDetector.isLowLevel()) { + value = setYearLowLevel(value, year); + return; + } JSDate date = JSDate.create(value); date.setFullYear(year + 1900); - this.value = (long) date.getTime(); + value = (long) date.getTime(); } + @Import(name = "teavm_date_setYear") + @NoSideEffects + private static native long setYearLowLevel(long date, int year); + @Deprecated public int getMonth() { + if (PlatformDetector.isLowLevel()) { + return getMonthLowLevel(value); + } return JSDate.create(value).getMonth(); } + @Import(name = "teavm_date_getMonth") + @NoSideEffects + private static native int getMonthLowLevel(long date); + @Deprecated public void setMonth(int month) { + if (PlatformDetector.isLowLevel()) { + value = setMonthLowLevel(value, month); + return; + } JSDate date = JSDate.create(value); date.setMonth(month); - this.value = (long) date.getTime(); + value = (long) date.getTime(); } + @Import(name = "teavm_date_setMonth") + @NoSideEffects + private static native long setMonthLowLevel(long date, int month); + @Deprecated public int getDate() { + if (PlatformDetector.isLowLevel()) { + return getDateLowLevel(value); + } return JSDate.create(value).getDate(); } + @Import(name = "teavm_date_getDate") + @NoSideEffects + private static native int getDateLowLevel(long date); + @Deprecated public void setDate(int date) { + if (PlatformDetector.isLowLevel()) { + value = setDateLowLevel(value, date); + return; + } JSDate d = JSDate.create(value); d.setDate(date); this.value = (long) d.getTime(); } + @Import(name = "teavm_date_setDate") + @NoSideEffects + private static native int setDateLowLevel(long target, int date); + @Deprecated public int getDay() { + if (PlatformDetector.isLowLevel()) { + return getDayLowLevel(value); + } return JSDate.create(value).getDay(); } + @Import(name = "teavm_date_getDay") + public static native int getDayLowLevel(long date); + @Deprecated public int getHours() { + if (PlatformDetector.isLowLevel()) { + return getHoursLowLevel(value); + } return JSDate.create(value).getHours(); } + @Import(name = "teavm_date_getHours") + @NoSideEffects + private static native int getHoursLowLevel(long date); + @Deprecated public void setHours(int hours) { + if (PlatformDetector.isLowLevel()) { + value = setHoursLowLevel(value, hours); + return; + } JSDate date = JSDate.create(value); date.setHours(hours); - this.value = (long) date.getTime(); + value = (long) date.getTime(); } + @Import(name = "teavm_date_setHours") + @NoSideEffects + private static native int setHoursLowLevel(long date, int hours); + @Deprecated public int getMinutes() { + if (PlatformDetector.isLowLevel()) { + return getMinutesLowLevel(value); + } return JSDate.create(value).getMinutes(); } + @Import(name = "teavm_date_getMinutes") + @NoSideEffects + private static native int getMinutesLowLevel(long date); + @Deprecated public void setMinutes(int minutes) { + if (PlatformDetector.isLowLevel()) { + value = setMinutesLowLevel(value, minutes); + return; + } JSDate date = JSDate.create(value); date.setMinutes(minutes); this.value = (long) date.getTime(); } + @Import(name = "teavm_date_setMinutes") + @NoSideEffects + private static native int setMinutesLowLevel(long date, int minutes); + @Deprecated public int getSeconds() { + if (PlatformDetector.isLowLevel()) { + return getSecondsLowLevel(value); + } return JSDate.create(value).getSeconds(); } + @Import(name = "teavm_date_getSeconds") + @NoSideEffects + private static native int getSecondsLowLevel(long date); + @Deprecated public void setSeconds(int seconds) { + if (PlatformDetector.isLowLevel()) { + value = setSecondsLowLevel(value, seconds); + return; + } JSDate date = JSDate.create(value); date.setSeconds(seconds); this.value = (long) date.getTime(); } + @Import(name = "teavm_date_setSeconds") + @NoSideEffects + private static native int setSecondsLowLevel(long date, int seconds); + public long getTime() { return value; } @@ -186,12 +318,16 @@ public class TDate implements TComparable { @Override public String toString() { if (PlatformDetector.isLowLevel()) { - return ""; + return toStringLowLevel(value); } else { return JSDate.create(value).stringValue(); } } + @Import(name = "teavm_date_format") + @NoSideEffects + private static native String toStringLowLevel(long date); + @Deprecated public String toLocaleString() { return JSDate.create(value).toLocaleFormat("%c"); diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 64bebfb76..99d1a800c 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -347,8 +347,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { } emitResource(runtimeHeaderWriter, "runtime.h"); - ClassGenerator classGenerator = new ClassGenerator(context, controller.getUnprocessedClassSource(), - tagRegistry, decompiler); + ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler); IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl( controller.getUnprocessedClassSource(), controller.getClassLoader(), controller.getServices(), controller.getProperties()); @@ -365,6 +364,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { OutputFileUtil.write(runtimeHeaderWriter, "runtime.h", buildTarget); copyResource("stringhash.c", buildTarget); copyResource("references.c", buildTarget); + copyResource("date.c", buildTarget); generateCallSites(buildTarget, context, classes.getClassNames()); generateStrings(buildTarget, context); @@ -637,6 +637,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { includes.includePath("stringhash.c"); includes.includePath("strings.c"); includes.includePath("callsites.c"); + includes.includePath("references.c"); + includes.includePath("date.c"); for (String className : classes.getClassNames()) { includes.includePath(ClassGenerator.fileName(className) + ".c"); 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 006050004..ee83423a1 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 @@ -38,7 +38,6 @@ import org.teavm.model.AnnotationHolder; import org.teavm.model.BasicBlock; import org.teavm.model.ClassHolder; import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldHolder; import org.teavm.model.FieldReader; @@ -78,7 +77,6 @@ public class ClassGenerator { )); private GenerationContext context; - private ClassReaderSource unprocessedClassSource; private Decompiler decompiler; private TagRegistry tagRegistry; private CodeGenerator codeGenerator; @@ -91,10 +89,8 @@ public class ClassGenerator { private IncludeManager includes; private IncludeManager headerIncludes; - public ClassGenerator(GenerationContext context, ClassReaderSource unprocessedClassSource, - TagRegistry tagRegistry, Decompiler decompiler) { + public ClassGenerator(GenerationContext context, TagRegistry tagRegistry, Decompiler decompiler) { this.context = context; - this.unprocessedClassSource = unprocessedClassSource; this.tagRegistry = tagRegistry; this.decompiler = decompiler; } @@ -436,7 +432,7 @@ public class ClassGenerator { String className = null; if (type instanceof ValueType.Object) { className = ((ValueType.Object) type).getClassName(); - generateVirtualTableStructure(unprocessedClassSource.get(className)); + generateVirtualTableStructure(className); } else if (type instanceof ValueType.Array) { className = "java.lang.Object"; } @@ -633,20 +629,17 @@ public class ClassGenerator { codeWriter.println(".init = " + initFunction); } - private void generateVirtualTableStructure(ClassReader cls) { - if (cls == null) { - return; - } - String name = context.getNames().forClassClass(cls.getName()); + private void generateVirtualTableStructure(String className) { + String name = context.getNames().forClassClass(className); headerWriter.print("typedef struct ").print(name).println(" {").indent(); headerWriter.println("TeaVM_Class parent;"); - VirtualTable virtualTable = context.getVirtualTableProvider().lookup(cls.getName()); + VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className); if (virtualTable != null) { for (VirtualTableEntry entry : virtualTable.getEntries().values()) { String methodName = context.getNames().forVirtualMethod( - new MethodReference(cls.getName(), entry.getMethod())); + new MethodReference(className, entry.getMethod())); headerWriter.printType(entry.getMethod().getResultType()) .print(" (*").print(methodName).print(")("); codeGenerator.generateMethodParameters(headerWriter, entry.getMethod(), false, false); diff --git a/core/src/main/resources/org/teavm/backend/c/date.c b/core/src/main/resources/org/teavm/backend/c/date.c new file mode 100644 index 000000000..5d1029a0c --- /dev/null +++ b/core/src/main/resources/org/teavm/backend/c/date.c @@ -0,0 +1,152 @@ +#include "runtime.h" + +#define _XOPEN_SOURCE +#define __USE_XOPEN +#define _GNU_SOURCE + +#include + +static time_t teavm_epochStart; +static struct tm teavm_epochStartTm; +static char teavm_date_formatBuffer[512]; +static char* teavm_date_defaultFormat = "%a %b %d %H:%M:%S %Z %Y"; + +void teavm_date_init() { + struct tm epochStart = { + .tm_year = 70, + .tm_mon = 0, + .tm_mday = 1, + .tm_hour = 0, + .tm_min = 0, + .tm_sec = 0, + .tm_isdst = -1 + }; + teavm_epochStart = timegm(&epochStart); + localtime_r(&teavm_epochStart, &teavm_epochStartTm); +} + +inline static int64_t teavm_date_timestamp(struct tm *t) { + time_t result = mktime(t); + return (int64_t) (1000 * difftime(result, teavm_epochStart)); +} + +inline static struct tm* teavm_date_decompose(int64_t timestamp, struct tm *t) { + *t = teavm_epochStartTm; + t->tm_sec += timestamp / 1000; + mktime(t); + return t; +} + +int64_t teavm_date_create(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t minute, int32_t second) { + struct tm t = { + .tm_year = year, + .tm_mon = month, + .tm_mday = day, + .tm_hour = hour, + .tm_min = minute, + .tm_sec = second, + .tm_isdst = -1 + }; + return teavm_date_timestamp(&t); +} + +int64_t teavm_date_createUtc(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t minute, int32_t second) { + struct tm t = { + .tm_year = year, + .tm_mon = month, + .tm_mday = day, + .tm_hour = hour, + .tm_min = minute, + .tm_sec = second, + .tm_isdst = -1 + }; + time_t result = timegm(&t); + return (int64_t) (1000 * difftime(result, teavm_epochStart)); +} + +int64_t teavm_date_parse(char* s) { + struct tm t; + strptime(s, teavm_date_defaultFormat, &t); + time_t result = mktime(&t); + return (int64_t) (1000 * difftime(result, teavm_epochStart)); +} + +int32_t teavm_date_getYear(int64_t time) { + struct tm t; + return (int32_t) teavm_date_decompose(time, &t)->tm_year; +} + +int64_t teavm_date_setYear(int64_t time, int32_t year) { + struct tm t; + teavm_date_decompose(time, &t)->tm_year = year; + return teavm_date_timestamp(&t); +} + +int32_t teavm_date_getMonth(int64_t time) { + struct tm t; + return (int32_t) teavm_date_decompose(time, &t)->tm_mon; +} + +int64_t teavm_date_setMonth(int64_t time, int32_t month) { + struct tm t; + teavm_date_decompose(time, &t)->tm_mon = month; + return teavm_date_timestamp(&t); +} + +int32_t teavm_date_getDate(int64_t time) { + struct tm t; + return (int32_t) teavm_date_decompose(time, &t)->tm_mday; +} + +int64_t teavm_date_setDate(int64_t time, int32_t date) { + struct tm t; + teavm_date_decompose(time, &t)->tm_mday = date; + return teavm_date_timestamp(&t); +} + +int32_t teavm_date_getDay(int64_t time) { + struct tm t; + return (int32_t) teavm_date_decompose(time, &t)->tm_wday; +} + +int32_t teavm_date_getHours(int64_t time) { + struct tm t; + return (int32_t) teavm_date_decompose(time, &t)->tm_hour; +} + +int64_t teavm_date_setHours(int64_t time, int32_t hours) { + struct tm t; + teavm_date_decompose(time, &t)->tm_hour = hours; + return teavm_date_timestamp(&t); +} + +int32_t teavm_date_getMinutes(int64_t time) { + struct tm t; + return (int32_t) teavm_date_decompose(time, &t)->tm_min; +} + +int64_t teavm_date_setMinutes(int64_t time, int32_t minutes) { + struct tm t; + teavm_date_decompose(time, &t)->tm_min = minutes; + return teavm_date_timestamp(&t); +} + +int32_t teavm_date_getSeconds(int64_t time) { + struct tm t; + return (int32_t) teavm_date_decompose(time, &t)->tm_sec; +} + +int64_t teavm_date_setSeconds(int64_t time, int32_t seconds) { + struct tm t; + teavm_date_decompose(time, &t)->tm_sec = seconds; + return teavm_date_timestamp(&t); +} + +char* teavm_date_format(int64_t time) { + struct tm t; + t = teavm_epochStartTm; + t.tm_sec += time / 1000; + mktime(&t); + strftime(teavm_date_formatBuffer, 512, teavm_date_defaultFormat, &t); + return teavm_date_formatBuffer; +} \ No newline at end of file diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.h b/core/src/main/resources/org/teavm/backend/c/runtime.h index ce3f007c6..f87c59e7a 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.h +++ b/core/src/main/resources/org/teavm/backend/c/runtime.h @@ -275,4 +275,23 @@ extern TeaVM_Object* teavm_reference_get(TeaVM_Reference*); extern TeaVM_Reference* teavm_reference_poll(TeaVM_ReferenceQueue*); -extern void teavm_reference_init(TeaVM_Reference*, TeaVM_Object*, TeaVM_ReferenceQueue*); \ No newline at end of file +extern void teavm_reference_init(TeaVM_Reference*, TeaVM_Object*, TeaVM_ReferenceQueue*); + +extern void teavm_date_init(); +extern int64_t teavm_date_create(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t); +extern int64_t teavm_date_createUtc(int32_t,int32_t,int32_t,int32_t,int32_t,int32_t); +extern int64_t teavm_date_parse(char* s); +extern int32_t teavm_date_getYear(int64_t); +extern int64_t teavm_date_setYear(int64_t,int32_t); +extern int32_t teavm_date_getMonth(int64_t); +extern int64_t teavm_date_setMonth(int64_t,int32_t); +extern int32_t teavm_date_getDate(int64_t); +extern int64_t teavm_date_setDate(int64_t,int32_t); +extern int32_t teavm_date_getDay(int64_t); +extern int32_t teavm_date_getHours(int64_t); +extern int64_t teavm_date_setHours(int64_t,int32_t); +extern int32_t teavm_date_getMinutes(int64_t); +extern int64_t teavm_date_setMinutes(int64_t,int32_t); +extern int32_t teavm_date_getSeconds(int64_t); +extern int64_t teavm_date_setSeconds(int64_t,int32_t); +extern char* teavm_date_format(int64_t); \ No newline at end of file