mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-10 08:54:11 -08:00
Add support of embedded time zone decoding
This commit is contained in:
parent
a6287b6cfd
commit
05c74a2d8a
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl.tz;
|
package org.teavm.classlib.impl.tz;
|
||||||
|
|
||||||
import org.teavm.classlib.impl.Base46;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Improves the performance of requesting time zone offsets and name keys by
|
* Improves the performance of requesting time zone offsets and name keys by
|
||||||
* caching the results. Time zones that have simple rules or are fixed should
|
* caching the results. Time zones that have simple rules or are fixed should
|
||||||
|
@ -64,7 +62,6 @@ public class CachedDateTimeZone extends StorableDateTimeZone {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(StringBuilder sb) {
|
public void write(StringBuilder sb) {
|
||||||
Base46.encodeUnsigned(sb, CACHED);
|
|
||||||
iZone.write(sb);
|
iZone.write(sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Calendar;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import org.teavm.classlib.impl.Base46;
|
import org.teavm.classlib.impl.Base46;
|
||||||
|
import org.teavm.classlib.impl.CharFlow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DateTimeZoneBuilder allows complex DateTimeZones to be constructed. Since
|
* DateTimeZoneBuilder allows complex DateTimeZones to be constructed. Since
|
||||||
|
@ -287,7 +288,7 @@ public class DateTimeZoneBuilder {
|
||||||
/**
|
/**
|
||||||
* Supports setting fields of year and moving between transitions.
|
* Supports setting fields of year and moving between transitions.
|
||||||
*/
|
*/
|
||||||
private static final class OfYear {
|
static final class OfYear {
|
||||||
// Is 'u', 'w', or 's'.
|
// Is 'u', 'w', or 's'.
|
||||||
final char iMode;
|
final char iMode;
|
||||||
|
|
||||||
|
@ -317,14 +318,23 @@ public class DateTimeZoneBuilder {
|
||||||
|
|
||||||
public void write(StringBuilder sb) {
|
public void write(StringBuilder sb) {
|
||||||
sb.append(iMode);
|
sb.append(iMode);
|
||||||
Base46.encodeUnsigned(sb, iDayOfMonth);
|
|
||||||
Base46.encodeUnsigned(sb, iMonthOfYear);
|
Base46.encodeUnsigned(sb, iMonthOfYear);
|
||||||
Base46.encode(sb, iDayOfMonth);
|
Base46.encodeUnsigned(sb, iDayOfMonth);
|
||||||
Base46.encode(sb, iDayOfWeek);
|
Base46.encode(sb, iDayOfWeek);
|
||||||
sb.append(iAdvance ? 'y' : 'n');
|
sb.append(iAdvance ? 'y' : 'n');
|
||||||
StorableDateTimeZone.writeUnsignedTime(sb, iMillisOfDay);
|
StorableDateTimeZone.writeUnsignedTime(sb, iMillisOfDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static OfYear read(CharFlow flow) {
|
||||||
|
char mode = flow.characters[flow.pointer++];
|
||||||
|
int monthOfYear = Base46.decodeUnsigned(flow);
|
||||||
|
int dayOfMonth = Base46.decodeUnsigned(flow);
|
||||||
|
int dayOfWeek = Base46.decode(flow);
|
||||||
|
boolean advance = flow.characters[flow.pointer++] == 'y';
|
||||||
|
int millisOfDay = (int)StorableDateTimeZone.readUnsignedTime(flow);
|
||||||
|
return new OfYear(mode, monthOfYear, dayOfMonth, dayOfWeek, advance, millisOfDay);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param standardOffset standard offset just before instant
|
* @param standardOffset standard offset just before instant
|
||||||
*/
|
*/
|
||||||
|
@ -342,6 +352,10 @@ public class DateTimeZoneBuilder {
|
||||||
calendar.setTimeInMillis(0);
|
calendar.setTimeInMillis(0);
|
||||||
calendar.set(Calendar.YEAR, year);
|
calendar.set(Calendar.YEAR, year);
|
||||||
calendar.set(Calendar.MONTH, iMonthOfYear - 1);
|
calendar.set(Calendar.MONTH, iMonthOfYear - 1);
|
||||||
|
calendar.set(Calendar.HOUR, 0);
|
||||||
|
calendar.set(Calendar.MINUTE, 0);
|
||||||
|
calendar.set(Calendar.SECOND, 0);
|
||||||
|
calendar.set(Calendar.MILLISECOND, 0);
|
||||||
calendar.add(Calendar.MILLISECOND, iMillisOfDay);
|
calendar.add(Calendar.MILLISECOND, iMillisOfDay);
|
||||||
setDayOfMonth(calendar);
|
setDayOfMonth(calendar);
|
||||||
|
|
||||||
|
@ -350,7 +364,7 @@ public class DateTimeZoneBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert from local time to UTC.
|
// Convert from local time to UTC.
|
||||||
return calendar.getTimeInMillis() - offset;
|
return calendar.getTimeInMillis() + calendar.get(Calendar.ZONE_OFFSET) - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -381,13 +395,13 @@ public class DateTimeZoneBuilder {
|
||||||
setDayOfMonthNext(calendar);
|
setDayOfMonthNext(calendar);
|
||||||
|
|
||||||
if (iDayOfWeek == 0) {
|
if (iDayOfWeek == 0) {
|
||||||
if (calendar.getTimeInMillis() <= instant) {
|
if (calendar.getTimeInMillis() + calendar.get(Calendar.ZONE_OFFSET) <= instant) {
|
||||||
calendar.add(Calendar.YEAR, 1);
|
calendar.add(Calendar.YEAR, 1);
|
||||||
setDayOfMonthNext(calendar);
|
setDayOfMonthNext(calendar);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setDayOfWeek(calendar);
|
setDayOfWeek(calendar);
|
||||||
if (calendar.getTimeInMillis() <= instant) {
|
if (calendar.getTimeInMillis() + calendar.get(Calendar.ZONE_OFFSET) <= instant) {
|
||||||
calendar.add(Calendar.YEAR, 1);
|
calendar.add(Calendar.YEAR, 1);
|
||||||
calendar.set(Calendar.MONTH, iMonthOfYear - 1);
|
calendar.set(Calendar.MONTH, iMonthOfYear - 1);
|
||||||
setDayOfMonthNext(calendar);
|
setDayOfMonthNext(calendar);
|
||||||
|
@ -396,7 +410,7 @@ public class DateTimeZoneBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert from local time to UTC.
|
// Convert from local time to UTC.
|
||||||
return calendar.getTimeInMillis() - offset;
|
return calendar.getTimeInMillis() + calendar.get(Calendar.ZONE_OFFSET) - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -428,13 +442,13 @@ public class DateTimeZoneBuilder {
|
||||||
setDayOfMonthPrevious(calendar);
|
setDayOfMonthPrevious(calendar);
|
||||||
|
|
||||||
if (iDayOfWeek == 0) {
|
if (iDayOfWeek == 0) {
|
||||||
if (calendar.getTimeInMillis() >= instant) {
|
if (calendar.getTimeInMillis() + calendar.get(Calendar.ZONE_OFFSET) >= instant) {
|
||||||
calendar.add(Calendar.YEAR, -1);
|
calendar.add(Calendar.YEAR, -1);
|
||||||
setDayOfMonthPrevious(calendar);
|
setDayOfMonthPrevious(calendar);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setDayOfWeek(calendar);
|
setDayOfWeek(calendar);
|
||||||
if (calendar.getTimeInMillis() >= instant) {
|
if (calendar.getTimeInMillis() + calendar.get(Calendar.ZONE_OFFSET) >= instant) {
|
||||||
calendar.add(Calendar.YEAR, -1);
|
calendar.add(Calendar.YEAR, -1);
|
||||||
calendar.set(Calendar.MONTH, iMonthOfYear - 1);
|
calendar.set(Calendar.MONTH, iMonthOfYear - 1);
|
||||||
setDayOfMonthPrevious(calendar);
|
setDayOfMonthPrevious(calendar);
|
||||||
|
@ -443,7 +457,7 @@ public class DateTimeZoneBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert from local time to UTC.
|
// Convert from local time to UTC.
|
||||||
return calendar.getTimeInMillis() - offset;
|
return calendar.getTimeInMillis() + calendar.get(Calendar.ZONE_OFFSET) - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -455,6 +469,7 @@ public class DateTimeZoneBuilder {
|
||||||
calendar.add(Calendar.YEAR, 1);
|
calendar.add(Calendar.YEAR, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setDayOfMonth(calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -466,6 +481,7 @@ public class DateTimeZoneBuilder {
|
||||||
calendar.add(Calendar.YEAR, -1);
|
calendar.add(Calendar.YEAR, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setDayOfMonth(calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDayOfMonth(Calendar calendar) {
|
private void setDayOfMonth(Calendar calendar) {
|
||||||
|
@ -499,7 +515,7 @@ public class DateTimeZoneBuilder {
|
||||||
/**
|
/**
|
||||||
* Extends OfYear with a nameKey and savings.
|
* Extends OfYear with a nameKey and savings.
|
||||||
*/
|
*/
|
||||||
private static final class Recurrence {
|
static final class Recurrence {
|
||||||
final OfYear iOfYear;
|
final OfYear iOfYear;
|
||||||
final int iSaveMillis;
|
final int iSaveMillis;
|
||||||
|
|
||||||
|
@ -534,6 +550,12 @@ public class DateTimeZoneBuilder {
|
||||||
iOfYear.write(sb);
|
iOfYear.write(sb);
|
||||||
StorableDateTimeZone.writeTime(sb, iSaveMillis);
|
StorableDateTimeZone.writeTime(sb, iSaveMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Recurrence read(CharFlow flow) {
|
||||||
|
OfYear ofYear = OfYear.read(flow);
|
||||||
|
int saveMillis = (int)StorableDateTimeZone.readTime(flow);
|
||||||
|
return new Recurrence(ofYear, saveMillis);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -585,7 +607,7 @@ public class DateTimeZoneBuilder {
|
||||||
calendar.setTimeInMillis(0);
|
calendar.setTimeInMillis(0);
|
||||||
calendar.set(Calendar.YEAR, iFromYear);
|
calendar.set(Calendar.YEAR, iFromYear);
|
||||||
// First advance instant to start of from year.
|
// First advance instant to start of from year.
|
||||||
testInstant = calendar.getTimeInMillis() - wallOffset;
|
testInstant = calendar.getTimeInMillis() + calendar.get(Calendar.ZONE_OFFSET) - wallOffset;
|
||||||
// Back off one millisecond to account for next recurrence
|
// Back off one millisecond to account for next recurrence
|
||||||
// being exactly at the beginning of the year.
|
// being exactly at the beginning of the year.
|
||||||
testInstant -= 1;
|
testInstant -= 1;
|
||||||
|
@ -878,13 +900,12 @@ public class DateTimeZoneBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class DSTZone extends StorableDateTimeZone {
|
static final class DSTZone extends StorableDateTimeZone {
|
||||||
final int iStandardOffset;
|
final int iStandardOffset;
|
||||||
final Recurrence iStartRecurrence;
|
final Recurrence iStartRecurrence;
|
||||||
final Recurrence iEndRecurrence;
|
final Recurrence iEndRecurrence;
|
||||||
|
|
||||||
DSTZone(String id, int standardOffset,
|
DSTZone(String id, int standardOffset, Recurrence startRecurrence, Recurrence endRecurrence) {
|
||||||
Recurrence startRecurrence, Recurrence endRecurrence) {
|
|
||||||
super(id);
|
super(id);
|
||||||
iStandardOffset = standardOffset;
|
iStandardOffset = standardOffset;
|
||||||
iStartRecurrence = startRecurrence;
|
iStartRecurrence = startRecurrence;
|
||||||
|
@ -989,7 +1010,7 @@ public class DateTimeZoneBuilder {
|
||||||
end = instant;
|
end = instant;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((start > end) ? start : end) - 1;
|
return (start > end ? start : end) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recurrence findMatchingRecurrence(long instant) {
|
private Recurrence findMatchingRecurrence(long instant) {
|
||||||
|
@ -1031,9 +1052,16 @@ public class DateTimeZoneBuilder {
|
||||||
iStartRecurrence.write(sb);
|
iStartRecurrence.write(sb);
|
||||||
iEndRecurrence.write(sb);
|
iEndRecurrence.write(sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DSTZone readZone(String id, CharFlow flow) {
|
||||||
|
int standardOffset = (int)readTime(flow);
|
||||||
|
Recurrence startRecurrence = Recurrence.read(flow);
|
||||||
|
Recurrence endRecurrence = Recurrence.read(flow);
|
||||||
|
return new DSTZone(id, standardOffset, startRecurrence, endRecurrence);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class PrecalculatedZone extends StorableDateTimeZone {
|
static final class PrecalculatedZone extends StorableDateTimeZone {
|
||||||
/**
|
/**
|
||||||
* Factory to create instance from builder.
|
* Factory to create instance from builder.
|
||||||
*
|
*
|
||||||
|
@ -1097,7 +1125,7 @@ public class DateTimeZoneBuilder {
|
||||||
@Override
|
@Override
|
||||||
public void write(StringBuilder sb) {
|
public void write(StringBuilder sb) {
|
||||||
int start = 0;
|
int start = 0;
|
||||||
while (start + 1 < iTransitions.length && iTransitions[start + 1] < 0) {
|
while (start + 1 < iTransitions.length && iTransitions[start + 1] < 631170000000L) {
|
||||||
++start;
|
++start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1111,7 +1139,7 @@ public class DateTimeZoneBuilder {
|
||||||
|
|
||||||
writeTime(sb, transitions[start]);
|
writeTime(sb, transitions[start]);
|
||||||
for (int i = start + 1; i < transitions.length; ++i) {
|
for (int i = start + 1; i < transitions.length; ++i) {
|
||||||
writeTime(sb, transitions[i] - transitions[i - 1] - (365 * 3600 * 1000));
|
writeTime(sb, transitions[i] - transitions[i - 1] - (365 * 3600 * 1000 / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
writeTimeArray(sb, Arrays.copyOfRange(iWallOffsets, start, transitions.length));
|
writeTimeArray(sb, Arrays.copyOfRange(iWallOffsets, start, transitions.length));
|
||||||
|
@ -1125,6 +1153,31 @@ public class DateTimeZoneBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static StorableDateTimeZone readZone(String id, CharFlow flow) {
|
||||||
|
int length = Base46.decodeUnsigned(flow);
|
||||||
|
long[] transitions = new long[length];
|
||||||
|
int[] wallOffsets = new int[length];
|
||||||
|
int[] standardOffsets = new int[length];
|
||||||
|
|
||||||
|
transitions[0] = readTime(flow);
|
||||||
|
for (int i = 1; i < length; ++i) {
|
||||||
|
transitions[i] = transitions[i - 1] + readTime(flow) + 365 * 3600 * 1000 / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
readTimeArray(flow, wallOffsets);
|
||||||
|
readTimeArray(flow, standardOffsets);
|
||||||
|
|
||||||
|
DSTZone tailZone;
|
||||||
|
if (flow.characters[flow.pointer++] == 'y') {
|
||||||
|
tailZone = DSTZone.readZone(id, flow);
|
||||||
|
} else {
|
||||||
|
tailZone = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrecalculatedZone result = new PrecalculatedZone(id, transitions, wallOffsets, standardOffsets, tailZone);
|
||||||
|
return result.isCachable() ? CachedDateTimeZone.forZone(result) : result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getOffset(long instant) {
|
public int getOffset(long instant) {
|
||||||
long[] transitions = iTransitions;
|
long[] transitions = iTransitions;
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 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.classlib.impl.tz;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.teavm.classlib.impl.Base46;
|
||||||
|
import org.teavm.classlib.impl.CharFlow;
|
||||||
|
import org.teavm.platform.metadata.MetadataProvider;
|
||||||
|
import org.teavm.platform.metadata.ResourceMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
public class DateTimeZoneProvider {
|
||||||
|
private static Map<String, DateTimeZone> cache = new HashMap<>();
|
||||||
|
|
||||||
|
public static DateTimeZone getTimeZone(String id) {
|
||||||
|
if (!cache.containsKey(id)) {
|
||||||
|
cache.put(id, createTimeZone(id));
|
||||||
|
}
|
||||||
|
return cache.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTimeZone createTimeZone(String id) {
|
||||||
|
TimeZoneResource res = getTimeZoneResource(id);
|
||||||
|
if (res == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String data = res.getData();
|
||||||
|
CharFlow flow = new CharFlow(data.toCharArray());
|
||||||
|
if (Base46.decode(flow) == StorableDateTimeZone.ALIAS) {
|
||||||
|
String aliasId = data.substring(flow.pointer);
|
||||||
|
return getTimeZone(aliasId);
|
||||||
|
} else {
|
||||||
|
return StorableDateTimeZone.read(id, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] getIds() {
|
||||||
|
List<String> ids = new ArrayList<>();
|
||||||
|
for (String areaName : getResource().keys()) {
|
||||||
|
ResourceMap<TimeZoneResource> area = getResource().get(areaName);
|
||||||
|
for (String id : area.keys()) {
|
||||||
|
if (!areaName.isEmpty()) {
|
||||||
|
id = areaName + "/" + id;
|
||||||
|
}
|
||||||
|
ids.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids.toArray(new String[ids.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TimeZoneResource getTimeZoneResource(String id) {
|
||||||
|
String areaName;
|
||||||
|
String locationName;
|
||||||
|
int sepIndex = id.indexOf('/');
|
||||||
|
if (sepIndex >= 0) {
|
||||||
|
areaName = id.substring(0, sepIndex);
|
||||||
|
locationName = id.substring(sepIndex + 1);
|
||||||
|
} else {
|
||||||
|
areaName = "";
|
||||||
|
locationName = id;
|
||||||
|
}
|
||||||
|
ResourceMap<TimeZoneResource> area = getResource().get(areaName);
|
||||||
|
if (area == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return area.get(locationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MetadataProvider(TimeZoneGenerator.class)
|
||||||
|
private static native ResourceMap<ResourceMap<TimeZoneResource>> getResource();
|
||||||
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.classlib.impl.tz;
|
package org.teavm.classlib.impl.tz;
|
||||||
|
|
||||||
import org.teavm.classlib.impl.Base46;
|
import org.teavm.classlib.impl.Base46;
|
||||||
|
import org.teavm.classlib.impl.CharFlow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic DateTimeZone implementation that has a fixed name key and offsets.
|
* Basic DateTimeZone implementation that has a fixed name key and offsets.
|
||||||
|
@ -71,4 +72,10 @@ public final class FixedDateTimeZone extends StorableDateTimeZone {
|
||||||
writeTime(sb, iWallOffset);
|
writeTime(sb, iWallOffset);
|
||||||
writeTime(sb, iStandardOffset);
|
writeTime(sb, iStandardOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FixedDateTimeZone readZone(String id, CharFlow flow) {
|
||||||
|
int wallOffset = (int)readTime(flow);
|
||||||
|
int standardOffset = (int)readTime(flow);
|
||||||
|
return new FixedDateTimeZone(id, wallOffset, standardOffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,19 @@
|
||||||
package org.teavm.classlib.impl.tz;
|
package org.teavm.classlib.impl.tz;
|
||||||
|
|
||||||
import org.teavm.classlib.impl.Base46;
|
import org.teavm.classlib.impl.Base46;
|
||||||
|
import org.teavm.classlib.impl.CharFlow;
|
||||||
|
import org.teavm.classlib.impl.tz.DateTimeZoneBuilder.DSTZone;
|
||||||
|
import org.teavm.classlib.impl.tz.DateTimeZoneBuilder.PrecalculatedZone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
public abstract class StorableDateTimeZone extends DateTimeZone {
|
public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
public static int PRECALCULATED = 0;
|
public static final int PRECALCULATED = 0;
|
||||||
public static int FIXED = 1;
|
public static final int FIXED = 1;
|
||||||
public static int CACHED = 2;
|
public static final int DST = 3;
|
||||||
public static int DST = 3;
|
public static final int ALIAS = 4;
|
||||||
|
|
||||||
public StorableDateTimeZone(String id) {
|
public StorableDateTimeZone(String id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
@ -41,6 +44,15 @@ public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long readTime(CharFlow flow) {
|
||||||
|
long value = Base46.decodeLong(flow);
|
||||||
|
if ((value & 1) == 0) {
|
||||||
|
return (value >> 1) * 1800_000;
|
||||||
|
} else {
|
||||||
|
return (value >> 1) * 60_000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void writeUnsignedTime(StringBuilder sb, long time) {
|
public static void writeUnsignedTime(StringBuilder sb, long time) {
|
||||||
if (time % 1800_000 == 0) {
|
if (time % 1800_000 == 0) {
|
||||||
Base46.encodeUnsigned(sb, (int)((time / 1800_000) << 1));
|
Base46.encodeUnsigned(sb, (int)((time / 1800_000) << 1));
|
||||||
|
@ -49,6 +61,15 @@ public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long readUnsignedTime(CharFlow flow) {
|
||||||
|
long value = Base46.decodeUnsignedLong(flow);
|
||||||
|
if ((value & 1) == 0) {
|
||||||
|
return (value >>> 1) * 1800_000;
|
||||||
|
} else {
|
||||||
|
return (value >>> 1) * 60_000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void writeTimeArray(StringBuilder sb, int[] timeArray) {
|
public static void writeTimeArray(StringBuilder sb, int[] timeArray) {
|
||||||
int last = 0;
|
int last = 0;
|
||||||
for (int i = 1; i < timeArray.length; ++i) {
|
for (int i = 1; i < timeArray.length; ++i) {
|
||||||
|
@ -78,4 +99,37 @@ public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void readTimeArray(CharFlow flow, int[] array) {
|
||||||
|
int index = 0;
|
||||||
|
while (index < array.length) {
|
||||||
|
int count = Base46.decode(flow);
|
||||||
|
if (count >= 0) {
|
||||||
|
int t = (int)readTime(flow);
|
||||||
|
while (count-- > 0) {
|
||||||
|
array[index++] = t;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count = ~count;
|
||||||
|
while (count-- > 0) {
|
||||||
|
array[index++] = (int)readTime(flow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StorableDateTimeZone read(String id, String text) {
|
||||||
|
CharFlow flow = new CharFlow(text.toCharArray());
|
||||||
|
int type = Base46.decode(flow);
|
||||||
|
switch (type) {
|
||||||
|
case PRECALCULATED:
|
||||||
|
return PrecalculatedZone.readZone(id, flow);
|
||||||
|
case DST:
|
||||||
|
return DSTZone.readZone(id, flow);
|
||||||
|
case FIXED:
|
||||||
|
return FixedDateTimeZone.readZone(id, flow);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown zone type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,11 @@ import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
import org.teavm.classlib.impl.Base46;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.platform.metadata.MetadataGenerator;
|
import org.teavm.platform.metadata.MetadataGenerator;
|
||||||
import org.teavm.platform.metadata.MetadataGeneratorContext;
|
import org.teavm.platform.metadata.MetadataGeneratorContext;
|
||||||
|
@ -66,17 +68,22 @@ public class TimeZoneGenerator implements MetadataGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Error generating TimeZones", e);
|
throw new RuntimeException("Error generating time zones", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, StorableDateTimeZone> zoneMap = compiler.compile();
|
Map<String, StorableDateTimeZone> zoneMap = compiler.compile();
|
||||||
|
Map<StorableDateTimeZone, String> zones = new HashMap<>();
|
||||||
for (String id : zoneMap.keySet()) {
|
for (String id : zoneMap.keySet()) {
|
||||||
int sepIndex = id.indexOf('/');
|
int sepIndex = id.indexOf('/');
|
||||||
|
String areaName;
|
||||||
|
String locationName;
|
||||||
if (sepIndex < 0) {
|
if (sepIndex < 0) {
|
||||||
continue;
|
areaName = "";
|
||||||
|
locationName = id;
|
||||||
|
} else {
|
||||||
|
areaName = id.substring(0, sepIndex);
|
||||||
|
locationName = id.substring(sepIndex + 1);
|
||||||
}
|
}
|
||||||
String areaName = id.substring(0, sepIndex);
|
|
||||||
String locationName = id.substring(sepIndex + 1);
|
|
||||||
ResourceMap<TimeZoneResource> area = result.get(areaName);
|
ResourceMap<TimeZoneResource> area = result.get(areaName);
|
||||||
if (area == null) {
|
if (area == null) {
|
||||||
area = context.createResourceMap();
|
area = context.createResourceMap();
|
||||||
|
@ -86,7 +93,14 @@ public class TimeZoneGenerator implements MetadataGenerator {
|
||||||
StorableDateTimeZone tz = zoneMap.get(id);
|
StorableDateTimeZone tz = zoneMap.get(id);
|
||||||
TimeZoneResource tzRes = context.createResource(TimeZoneResource.class);
|
TimeZoneResource tzRes = context.createResource(TimeZoneResource.class);
|
||||||
StringBuilder data = new StringBuilder();
|
StringBuilder data = new StringBuilder();
|
||||||
|
String knownId = zones.get(tz);
|
||||||
|
if (knownId == null) {
|
||||||
tz.write(data);
|
tz.write(data);
|
||||||
|
zones.put(tz, id);
|
||||||
|
} else {
|
||||||
|
Base46.encode(data, StorableDateTimeZone.ALIAS);
|
||||||
|
data.append(knownId);
|
||||||
|
}
|
||||||
tzRes.setData(data.toString());
|
tzRes.setData(data.toString());
|
||||||
area.put(locationName, tzRes);
|
area.put(locationName, tzRes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2015 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.classlib.impl.tz;
|
|
||||||
|
|
||||||
import org.teavm.platform.metadata.MetadataProvider;
|
|
||||||
import org.teavm.platform.metadata.ResourceMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
public class TimeZoneResourceProvider {
|
|
||||||
public static TimeZoneResource getTimeZone(String id) {
|
|
||||||
int sepIndex = id.indexOf('/');
|
|
||||||
String areaName = id.substring(0, sepIndex);
|
|
||||||
String locationName = id.substring(sepIndex + 1);
|
|
||||||
ResourceMap<TimeZoneResource> area = getResource().get(areaName);
|
|
||||||
if (area == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return area.get(locationName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@MetadataProvider(TimeZoneGenerator.class)
|
|
||||||
private static native ResourceMap<ResourceMap<TimeZoneResource>> getResource();
|
|
||||||
}
|
|
|
@ -182,7 +182,7 @@ public class ZoneInfoCompiler {
|
||||||
|
|
||||||
long trans = transitions.get(i).longValue();
|
long trans = transitions.get(i).longValue();
|
||||||
|
|
||||||
/*if (trans - 1 != millis) {
|
if (trans - 1 != millis) {
|
||||||
System.out.println("*r* Error in " + tz.getID() + " "
|
System.out.println("*r* Error in " + tz.getID() + " "
|
||||||
+ new DateTime(millis,
|
+ new DateTime(millis,
|
||||||
ISOChronology.getInstanceUTC()) + " != "
|
ISOChronology.getInstanceUTC()) + " != "
|
||||||
|
@ -190,7 +190,7 @@ public class ZoneInfoCompiler {
|
||||||
ISOChronology.getInstanceUTC()));
|
ISOChronology.getInstanceUTC()));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -174,7 +174,7 @@ public class TClass<T> extends TObject {
|
||||||
return forName(name);
|
return forName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked" })
|
@SuppressWarnings({ "unchecked", "unused" })
|
||||||
public T newInstance() throws TInstantiationException, TIllegalAccessException {
|
public T newInstance() throws TInstantiationException, TIllegalAccessException {
|
||||||
Object instance = Platform.newInstance(platformClass);
|
Object instance = Platform.newInstance(platformClass);
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
|
|
|
@ -26,4 +26,6 @@ public interface ResourceMap<T extends Resource> extends Resource {
|
||||||
T get(String key);
|
T get(String key);
|
||||||
|
|
||||||
void put(String key, T value);
|
void put(String key, T value);
|
||||||
|
|
||||||
|
String[] keys();
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,4 +59,9 @@ class BuildTimeResourceMap<T extends Resource> implements ResourceMap<T>, Resour
|
||||||
}
|
}
|
||||||
writer.append('}').tokenBoundary();
|
writer.append('}').tokenBoundary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] keys() {
|
||||||
|
return data.keySet().toArray(new String[data.size()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,17 @@ final class ResourceAccessor {
|
||||||
|
|
||||||
public static native Resource get(Object obj, String propertyName);
|
public static native Resource get(Object obj, String propertyName);
|
||||||
|
|
||||||
|
public static native Object keys(Object obj);
|
||||||
|
|
||||||
|
public static String[] keysToStrings(Object keys) {
|
||||||
|
int sz = size(keys);
|
||||||
|
String[] result = new String[sz];
|
||||||
|
for (int i = 0; i < sz; ++i) {
|
||||||
|
result[i] = castToString(get(keys, i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public static native void put(Object obj, String propertyName, Object elem);
|
public static native void put(Object obj, String propertyName, Object elem);
|
||||||
|
|
||||||
public static native Resource get(Object obj, int index);
|
public static native Resource get(Object obj, int index);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2014 Alexey Andreev.
|
* Copyright 2015 Alexey Andreev.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,135 +16,22 @@
|
||||||
package org.teavm.platform.plugin;
|
package org.teavm.platform.plugin;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.teavm.javascript.ast.ConstantExpr;
|
import org.teavm.codegen.SourceWriter;
|
||||||
import org.teavm.javascript.ast.Expr;
|
import org.teavm.javascript.spi.Generator;
|
||||||
import org.teavm.javascript.spi.Injector;
|
import org.teavm.javascript.spi.GeneratorContext;
|
||||||
import org.teavm.javascript.spi.InjectorContext;
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
class ResourceAccessorGenerator implements Injector {
|
class ResourceAccessorGenerator implements Generator {
|
||||||
@Override
|
@Override
|
||||||
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||||
switch (methodRef.getName()) {
|
writer.append("var result = [];").softNewLine();
|
||||||
case "get":
|
writer.append("for (var key in ").append(context.getParameterName(1)).append(") {").indent().softNewLine();
|
||||||
case "getProperty":
|
writer.append("result.push(key);").softNewLine();
|
||||||
if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
|
writer.outdent().append("}").softNewLine();
|
||||||
context.writeExpr(context.getArgument(0));
|
writer.append("return result;").softNewLine();
|
||||||
context.getWriter().append('[');
|
|
||||||
context.writeExpr(context.getArgument(1));
|
|
||||||
context.getWriter().append(']');
|
|
||||||
} else {
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
writePropertyAccessor(context, context.getArgument(1));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "put":
|
|
||||||
context.getWriter().append('(');
|
|
||||||
if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
context.getWriter().append('[');
|
|
||||||
context.writeExpr(context.getArgument(1));
|
|
||||||
} else {
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
writePropertyAccessor(context, context.getArgument(1));
|
|
||||||
}
|
|
||||||
context.getWriter().ws().append('=').ws();
|
|
||||||
context.writeExpr(context.getArgument(2));
|
|
||||||
context.getWriter().append(')');
|
|
||||||
break;
|
|
||||||
case "add":
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
context.getWriter().append(".push(");
|
|
||||||
context.writeExpr(context.getArgument(1));
|
|
||||||
context.getWriter().append(')');
|
|
||||||
break;
|
|
||||||
case "has":
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
context.getWriter().append(".hasOwnProperty(");
|
|
||||||
writeStringExpr(context, context.getArgument(1));
|
|
||||||
context.getWriter().append(')');
|
|
||||||
break;
|
|
||||||
case "size":
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
context.getWriter().append(".length");
|
|
||||||
break;
|
|
||||||
case "castToInt":
|
|
||||||
case "castToShort":
|
|
||||||
case "castToByte":
|
|
||||||
case "castToBoolean":
|
|
||||||
case "castToFloat":
|
|
||||||
case "castToDouble":
|
|
||||||
case "castFromInt":
|
|
||||||
case "castFromShort":
|
|
||||||
case "castFromByte":
|
|
||||||
case "castFromBoolean":
|
|
||||||
case "castFromFloat":
|
|
||||||
case "castFromDouble":
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
break;
|
|
||||||
case "castToString":
|
|
||||||
context.getWriter().append('(');
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
context.getWriter().ws().append("!==").ws().append("null").ws().append("?").ws();
|
|
||||||
context.getWriter().append("$rt_str(");
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
context.getWriter().append(")").ws().append(':').ws().append("null)");
|
|
||||||
break;
|
|
||||||
case "castFromString":
|
|
||||||
context.getWriter().append('(');
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
context.getWriter().ws().append("!==").ws().append("null").ws().append("?").ws();
|
|
||||||
context.getWriter().append("$rt_ustr(");
|
|
||||||
context.writeExpr(context.getArgument(0));
|
|
||||||
context.getWriter().append(")").ws().append(':').ws().append("null)");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writePropertyAccessor(InjectorContext context, Expr property) throws IOException {
|
|
||||||
if (property instanceof ConstantExpr) {
|
|
||||||
String str = (String)((ConstantExpr)property).getValue();
|
|
||||||
if (str.isEmpty()) {
|
|
||||||
context.getWriter().append("[\"\"]");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isValidIndentifier(str)) {
|
|
||||||
context.getWriter().append(".").append(str);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context.getWriter().append("[$rt_ustr(");
|
|
||||||
context.writeExpr(property);
|
|
||||||
context.getWriter().append(")]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeStringExpr(InjectorContext context, Expr expr) throws IOException {
|
|
||||||
if (expr instanceof ConstantExpr) {
|
|
||||||
String str = (String)((ConstantExpr)expr).getValue();
|
|
||||||
context.getWriter().append('"');
|
|
||||||
context.writeEscaped(str);
|
|
||||||
context.getWriter().append('"');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context.getWriter().append("$rt_ustr(");
|
|
||||||
context.writeExpr(expr);
|
|
||||||
context.getWriter().append(")");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isValidIndentifier(String str) {
|
|
||||||
if (!Character.isJavaIdentifierStart(str.charAt(0))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = 1; i < str.length(); ++i) {
|
|
||||||
if (!Character.isJavaIdentifierPart(str.charAt(i))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2014 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.platform.plugin;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.teavm.javascript.ast.ConstantExpr;
|
||||||
|
import org.teavm.javascript.ast.Expr;
|
||||||
|
import org.teavm.javascript.spi.Injector;
|
||||||
|
import org.teavm.javascript.spi.InjectorContext;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
class ResourceAccessorInjector implements Injector {
|
||||||
|
@Override
|
||||||
|
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
|
||||||
|
switch (methodRef.getName()) {
|
||||||
|
case "get":
|
||||||
|
case "getProperty":
|
||||||
|
if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().append('[');
|
||||||
|
context.writeExpr(context.getArgument(1));
|
||||||
|
context.getWriter().append(']');
|
||||||
|
} else {
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
writePropertyAccessor(context, context.getArgument(1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "put":
|
||||||
|
context.getWriter().append('(');
|
||||||
|
if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().append('[');
|
||||||
|
context.writeExpr(context.getArgument(1));
|
||||||
|
} else {
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
writePropertyAccessor(context, context.getArgument(1));
|
||||||
|
}
|
||||||
|
context.getWriter().ws().append('=').ws();
|
||||||
|
context.writeExpr(context.getArgument(2));
|
||||||
|
context.getWriter().append(')');
|
||||||
|
break;
|
||||||
|
case "add":
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().append(".push(");
|
||||||
|
context.writeExpr(context.getArgument(1));
|
||||||
|
context.getWriter().append(')');
|
||||||
|
break;
|
||||||
|
case "has":
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().append(".hasOwnProperty(");
|
||||||
|
writeStringExpr(context, context.getArgument(1));
|
||||||
|
context.getWriter().append(')');
|
||||||
|
break;
|
||||||
|
case "size":
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().append(".length");
|
||||||
|
break;
|
||||||
|
case "castToInt":
|
||||||
|
case "castToShort":
|
||||||
|
case "castToByte":
|
||||||
|
case "castToBoolean":
|
||||||
|
case "castToFloat":
|
||||||
|
case "castToDouble":
|
||||||
|
case "castFromInt":
|
||||||
|
case "castFromShort":
|
||||||
|
case "castFromByte":
|
||||||
|
case "castFromBoolean":
|
||||||
|
case "castFromFloat":
|
||||||
|
case "castFromDouble":
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
break;
|
||||||
|
case "castToString":
|
||||||
|
context.getWriter().append('(');
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().ws().append("!==").ws().append("null").ws().append("?").ws();
|
||||||
|
context.getWriter().append("$rt_str(");
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().append(")").ws().append(':').ws().append("null)");
|
||||||
|
break;
|
||||||
|
case "castFromString":
|
||||||
|
context.getWriter().append('(');
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().ws().append("!==").ws().append("null").ws().append("?").ws();
|
||||||
|
context.getWriter().append("$rt_ustr(");
|
||||||
|
context.writeExpr(context.getArgument(0));
|
||||||
|
context.getWriter().append(")").ws().append(':').ws().append("null)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writePropertyAccessor(InjectorContext context, Expr property) throws IOException {
|
||||||
|
if (property instanceof ConstantExpr) {
|
||||||
|
String str = (String)((ConstantExpr)property).getValue();
|
||||||
|
if (str.isEmpty()) {
|
||||||
|
context.getWriter().append("[\"\"]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isValidIndentifier(str)) {
|
||||||
|
context.getWriter().append(".").append(str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.getWriter().append("[$rt_ustr(");
|
||||||
|
context.writeExpr(property);
|
||||||
|
context.getWriter().append(")]");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeStringExpr(InjectorContext context, Expr expr) throws IOException {
|
||||||
|
if (expr instanceof ConstantExpr) {
|
||||||
|
String str = (String)((ConstantExpr)expr).getValue();
|
||||||
|
context.getWriter().append('"');
|
||||||
|
context.writeEscaped(str);
|
||||||
|
context.getWriter().append('"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context.getWriter().append("$rt_ustr(");
|
||||||
|
context.writeExpr(expr);
|
||||||
|
context.getWriter().append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidIndentifier(String str) {
|
||||||
|
if (!Character.isJavaIdentifierStart(str.charAt(0))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < str.length(); ++i) {
|
||||||
|
if (!Character.isJavaIdentifierPart(str.charAt(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,10 +33,14 @@ class ResourceAccessorTransformer implements ClassHolderTransformer {
|
||||||
@Override
|
@Override
|
||||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||||
if (cls.getName().equals(ResourceAccessor.class.getName())) {
|
if (cls.getName().equals(ResourceAccessor.class.getName())) {
|
||||||
ResourceAccessorGenerator generator = new ResourceAccessorGenerator();
|
ResourceAccessorInjector injector = new ResourceAccessorInjector();
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
if (!method.getName().equals("<init>")) {
|
if (method.hasModifier(ElementModifier.NATIVE)) {
|
||||||
vm.add(method.getReference(), generator);
|
if (method.getName().equals("keys")) {
|
||||||
|
vm.add(method.getReference(), new ResourceAccessorGenerator());
|
||||||
|
} else {
|
||||||
|
vm.add(method.getReference(), injector);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,9 @@ class ResourceProgramTransformer {
|
||||||
MethodReference method = insn.getMethod();
|
MethodReference method = insn.getMethod();
|
||||||
if (method.getClassName().equals(ResourceArray.class.getName()) ||
|
if (method.getClassName().equals(ResourceArray.class.getName()) ||
|
||||||
method.getClassName().equals(ResourceMap.class.getName())) {
|
method.getClassName().equals(ResourceMap.class.getName())) {
|
||||||
|
if (method.getName().equals("keys")) {
|
||||||
|
return transformKeys(insn);
|
||||||
|
}
|
||||||
InvokeInstruction accessInsn = new InvokeInstruction();
|
InvokeInstruction accessInsn = new InvokeInstruction();
|
||||||
accessInsn.setType(InvocationType.SPECIAL);
|
accessInsn.setType(InvocationType.SPECIAL);
|
||||||
ValueType[] types = new ValueType[method.getDescriptor().parameterCount() + 2];
|
ValueType[] types = new ValueType[method.getDescriptor().parameterCount() + 2];
|
||||||
|
@ -96,6 +99,25 @@ class ResourceProgramTransformer {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Instruction> transformKeys(InvokeInstruction insn) {
|
||||||
|
Variable tmp = program.createVariable();
|
||||||
|
|
||||||
|
InvokeInstruction keysInsn = new InvokeInstruction();
|
||||||
|
keysInsn.setType(InvocationType.SPECIAL);
|
||||||
|
keysInsn.setMethod(new MethodReference(ResourceAccessor.class, "keys", Object.class, Object.class));
|
||||||
|
keysInsn.getArguments().add(insn.getInstance());
|
||||||
|
keysInsn.setReceiver(tmp);
|
||||||
|
|
||||||
|
InvokeInstruction transformInsn = new InvokeInstruction();
|
||||||
|
transformInsn.setType(InvocationType.SPECIAL);
|
||||||
|
transformInsn.setMethod(new MethodReference(ResourceAccessor.class, "keysToStrings",
|
||||||
|
Object.class, String[].class));
|
||||||
|
transformInsn.getArguments().add(tmp);
|
||||||
|
transformInsn.setReceiver(insn.getReceiver());
|
||||||
|
|
||||||
|
return Arrays.<Instruction>asList(keysInsn, transformInsn);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isSubclass(ClassReader cls, String superClass) {
|
private boolean isSubclass(ClassReader cls, String superClass) {
|
||||||
if (cls.getName().equals(superClass)) {
|
if (cls.getName().equals(superClass)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.teavm.classlib.java.nio.charset;
|
package org.teavm.classlib.java.nio.charset;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
@ -94,7 +93,7 @@ public class UTF8Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void decodeLongUTF8ByteArray() throws UnsupportedEncodingException {
|
public void decodeLongUTF8ByteArray() {
|
||||||
byte[] bytes = new byte[16384];
|
byte[] bytes = new byte[16384];
|
||||||
for (int i = 0; i < bytes.length;) {
|
for (int i = 0; i < bytes.length;) {
|
||||||
bytes[i++] = -16;
|
bytes[i++] = -16;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package org.teavm.classlib.java.util;
|
package org.teavm.classlib.java.util;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.teavm.classlib.impl.tz.TimeZoneResourceProvider;
|
import org.teavm.classlib.impl.tz.DateTimeZone;
|
||||||
|
import org.teavm.classlib.impl.tz.DateTimeZoneProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -11,6 +12,9 @@ import org.teavm.classlib.impl.tz.TimeZoneResourceProvider;
|
||||||
public class TimeZoneTest {
|
public class TimeZoneTest {
|
||||||
@Test
|
@Test
|
||||||
public void resourceProvided() {
|
public void resourceProvided() {
|
||||||
assertNotNull(TimeZoneResourceProvider.getTimeZone("Europe/Moscow"));
|
DateTimeZone tz = DateTimeZoneProvider.getTimeZone("Europe/Moscow");
|
||||||
|
assertEquals(1414274399999L, tz.previousTransition(1431781727159L));
|
||||||
|
assertEquals(1301183999999L, tz.previousTransition(1414274399999L));
|
||||||
|
assertEquals(1288479599999L, tz.previousTransition(1301183999999L));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user