C backend: implement support for ResourceArray and ResourceMap

This commit is contained in:
Alexey Andreev 2018-05-07 19:30:00 +03:00
parent 401a9ed212
commit f532801f38
8 changed files with 336 additions and 9 deletions

View File

@ -19,9 +19,11 @@ import java.lang.reflect.Array;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import org.teavm.backend.c.TeaVMCHost;
import org.teavm.backend.javascript.TeaVMJavaScriptHost; import org.teavm.backend.javascript.TeaVMJavaScriptHost;
import org.teavm.classlib.ReflectionSupplier; import org.teavm.classlib.ReflectionSupplier;
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor; import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
import org.teavm.classlib.impl.tz.DateTimeZoneProviderIntrinsic;
import org.teavm.classlib.impl.unicode.CLDRReader; import org.teavm.classlib.impl.unicode.CLDRReader;
import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener; import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener;
import org.teavm.interop.PlatformMarker; import org.teavm.interop.PlatformMarker;
@ -85,6 +87,11 @@ public class JCLPlugin implements TeaVMPlugin {
host.add(reflection); host.add(reflection);
host.add(new PlatformMarkerSupport(host.getPlatformTags())); host.add(new PlatformMarkerSupport(host.getPlatformTags()));
TeaVMCHost cHost = host.getExtension(TeaVMCHost.class);
if (cHost != null) {
cHost.addIntrinsic(context -> new DateTimeZoneProviderIntrinsic(context.getProperties()));
}
} }
TeaVMPluginUtil.handleNatives(host, Class.class); TeaVMPluginUtil.handleNatives(host, Class.class);

View File

@ -0,0 +1,59 @@
/*
* Copyright 2018 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.Properties;
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.c.intrinsic.Intrinsic;
import org.teavm.backend.c.intrinsic.IntrinsicContext;
import org.teavm.model.MethodReference;
public class DateTimeZoneProviderIntrinsic implements Intrinsic {
private Properties properties;
public DateTimeZoneProviderIntrinsic(Properties properties) {
this.properties = properties;
}
@Override
public boolean canHandle(MethodReference method) {
if (!method.getClassName().equals(DateTimeZoneProvider.class.getName())) {
return false;
}
switch (method.getName()) {
case "timeZoneDetectionEnabled":
case "getNativeOffset":
return true;
default:
return false;
}
}
@Override
public void apply(IntrinsicContext context, InvocationExpr invocation) {
switch (invocation.getMethod().getName()) {
case "timeZoneDetectionEnabled": {
boolean enabled = properties.getProperty("java.util.TimeZone.autodetect", "false").equals("true");
context.writer().print(enabled ? "1" : "0");
break;
}
case "getNativeOffset":
context.writer().print("teavm_timeZoneOffset()");
break;
}
}
}

View File

@ -353,6 +353,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) { private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
generateThrowCCE(context, writer); generateThrowCCE(context, writer);
generateAllocateStringArray(context, writer);
} }
private void generateThrowCCE(GenerationContext context, CodeWriter writer) { private void generateThrowCCE(GenerationContext context, CodeWriter writer) {
@ -364,6 +365,16 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
writer.outdent().println("}"); writer.outdent().println("}");
} }
private void generateAllocateStringArray(GenerationContext context, CodeWriter writer) {
writer.println("static JavaArray* teavm_allocateStringArray(int32_t size) {").indent();
String allocateArrayName = context.getNames().forMethod(new MethodReference(Allocator.class,
"allocateArray", RuntimeClass.class, int.class, Address.class));
String stringClassName = context.getNames().forClassInstance(ValueType.arrayOf(
ValueType.object(String.class.getName())));
writer.println("return (JavaArray*) " + allocateArrayName + "(&" + stringClassName + ", size);");
writer.outdent().println("}");
}
private void generateMain(GenerationContext context, CodeWriter writer, ListableClassHolderSource classes, private void generateMain(GenerationContext context, CodeWriter writer, ListableClassHolderSource classes,
Set<? extends ValueType> types) { Set<? extends ValueType> types) {
writer.println("int main(int argc, char** argv) {").indent(); writer.println("int main(int argc, char** argv) {").indent();

View File

@ -34,4 +34,6 @@ public interface IntrinsicContext {
MethodReference getCallingMethod(); MethodReference getCallingMethod();
StringPool getStringPool(); StringPool getStringPool();
} }

View File

@ -4,4 +4,50 @@ static inline int32_t instanceof(void* obj, int32_t (*cls)(JavaClass*)) {
static inline void* checkcast(void* obj, int32_t (*cls)(JavaClass*)) { static inline void* checkcast(void* obj, int32_t (*cls)(JavaClass*)) {
return obj == NULL || cls(CLASS_OF(obj)) ? obj : throwClassCastException(); return obj == NULL || cls(CLASS_OF(obj)) ? obj : throwClassCastException();
}
static int32_t teavm_hashCode(JavaString* string) {
int32_t hashCode = INT32_C(0);
int32_t length = string->characters->size;
char16_t* chars = ARRAY_DATA(string->characters, char16_t);
for (int32_t i = INT32_C(0); i < length; ++i) {
hashCode = 31 * hashCode + chars[i];
}
return hashCode;
}
static int32_t teavm_equals(JavaString* first, JavaString* second) {
if (first->characters->size != second->characters->size) {
return 0;
}
char16_t* firstChars = ARRAY_DATA(first->characters, char16_t);
char16_t* secondChars = ARRAY_DATA(second->characters, char16_t);
int32_t length = first->characters->size;
for (int32_t i = INT32_C(0); i < length; ++i) {
if (firstChars[i] != secondChars[i]) {
return 0;
}
}
return 1;
}
static JavaArray* teavm_resourceMapKeys(TeaVM_ResourceMap *map) {
int32_t size = 0;
for (int32_t i = 0; i < map->size; ++i) {
if (map->entries[i].key != NULL) {
size++;
}
}
int32_t index = 0;
void* array = teavm_allocateStringArray(size);
void** data = ARRAY_DATA(array, void*);
for (int32_t i = 0; i < map->size; ++i) {
if (map->entries[i].key != NULL) {
data[index++] = map->entries[i].key;
}
}
return array;
} }

View File

@ -114,6 +114,46 @@ static inline float TeaVM_getNaN() {
return NAN; return NAN;
} }
typedef struct {
int32_t size;
void* data[0];
} TeaVM_ResourceArray;
typedef struct {
JavaString* key;
void* value;
} TeaVM_ResourceMapEntry;
typedef struct {
int32_t size;
TeaVM_ResourceMapEntry entries[0];
} TeaVM_ResourceMap;
static int32_t teavm_hashCode(JavaString*);
static int32_t teavm_equals(JavaString*, JavaString*);
static JavaArray* teavm_allocateStringArray(int32_t size);
static TeaVM_ResourceMapEntry* teavm_lookupResource(TeaVM_ResourceMap *map, JavaString* string) {
uint32_t hashCode = teavm_hashCode(string);
for (int32_t i = 0; i < map->size; ++i) {
uint32_t index = (hashCode + i) % map->size;
if (map->entries[index].key == NULL) {
return NULL;
}
if (teavm_equals(map->entries[index].key, string)) {
return &map->entries[index];
}
}
return NULL;
}
static inline void* teavm_lookupResourceValue(TeaVM_ResourceMap *map, JavaString* string) {
TeaVM_ResourceMapEntry *entry = teavm_lookupResource(map, string);
return entry != NULL ? entry->value : NULL;
}
static JavaArray* teavm_resourceMapKeys(TeaVM_ResourceMap *);
static void TeaVM_beforeInit() { static void TeaVM_beforeInit() {
srand(time(NULL)); srand(time(NULL));
} }
@ -158,6 +198,13 @@ static int64_t currentTimeMillis() {
return time.tv_sec * 1000 + (int64_t) round(time.tv_nsec / 1000000); return time.tv_sec * 1000 + (int64_t) round(time.tv_nsec / 1000000);
} }
static int32_t teavm_timeZoneOffset() {
time_t t = time(NULL);
time_t local = mktime(localtime(&t));
time_t utc = mktime(gmtime(&t));
return difftime(utc, local) / 60;
}
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER

View File

@ -30,6 +30,8 @@ import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.MetadataGenerator; import org.teavm.platform.metadata.MetadataGenerator;
import org.teavm.platform.metadata.MetadataProvider; import org.teavm.platform.metadata.MetadataProvider;
import org.teavm.platform.metadata.Resource; import org.teavm.platform.metadata.Resource;
import org.teavm.platform.metadata.ResourceArray;
import org.teavm.platform.metadata.ResourceMap;
public class MetadataCIntrinsic implements Intrinsic { public class MetadataCIntrinsic implements Intrinsic {
private ClassReaderSource classSource; private ClassReaderSource classSource;
@ -89,11 +91,36 @@ public class MetadataCIntrinsic implements Intrinsic {
} }
private void writeValue(IntrinsicContext context, Object value) { private void writeValue(IntrinsicContext context, Object value) {
if (value instanceof String) { if (value == null) {
staticFieldInitWriter.print("NULL");
} else if (value instanceof String) {
int stringIndex = context.getStringPool().getStringIndex((String) value); int stringIndex = context.getStringPool().getStringIndex((String) value);
staticFieldInitWriter.print("(stringPool + " + stringIndex + ")"); staticFieldInitWriter.print("(JavaObject*) (stringPool + " + stringIndex + ")");
} else if (value instanceof Boolean) {
staticFieldInitWriter.print((Boolean) value ? "1" : "0");
} else if (value instanceof Integer) {
int n = (Integer) value;
if (n < 0) {
staticFieldInitWriter.print("-");
n = -n;
}
staticFieldInitWriter.print("INT32_C(" + n + ")");
} else if (value instanceof Long) {
long n = (Long) value;
if (n < 0) {
staticFieldInitWriter.print("-");
n = -n;
}
staticFieldInitWriter.print("INT64_C(" + n + ")");
} else if (value instanceof Byte || value instanceof Short || value instanceof Float
|| value instanceof Double) {
staticFieldInitWriter.print(value.toString());
} else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) { } else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) {
writeResource(context, (ResourceTypeDescriptorProvider) value); writeResource(context, (ResourceTypeDescriptorProvider) value);
} else if (value instanceof ResourceMap) {
writeResourceMap(context, (ResourceMap<?>) value);
} else if (value instanceof ResourceArray) {
writeResourceArray(context, (ResourceArray<?>) value);
} else { } else {
throw new IllegalArgumentException("Don't know how to write resource: " + value); throw new IllegalArgumentException("Don't know how to write resource: " + value);
} }
@ -126,7 +153,8 @@ public class MetadataCIntrinsic implements Intrinsic {
} }
for (Class<?> propertyType : structure.getPropertyTypes().values()) { for (Class<?> propertyType : structure.getPropertyTypes().values()) {
if (Resource.class.isAssignableFrom(propertyType)) { if (Resource.class.isAssignableFrom(propertyType) && !ResourceMap.class.isAssignableFrom(propertyType)
&& !ResourceArray.class.isAssignableFrom(propertyType)) {
ResourceTypeDescriptor propertyStructure = metadataContext.getTypeDescriptor( ResourceTypeDescriptor propertyStructure = metadataContext.getTypeDescriptor(
propertyType.asSubclass(Resource.class)); propertyType.asSubclass(Resource.class));
writeResourceStructure(context, propertyStructure); writeResourceStructure(context, propertyStructure);
@ -158,7 +186,7 @@ public class MetadataCIntrinsic implements Intrinsic {
} else if (cls == double.class) { } else if (cls == double.class) {
return "double"; return "double";
} else if (Resource.class.isAssignableFrom(cls)) { } else if (Resource.class.isAssignableFrom(cls)) {
return "&" + context.names().forClass(cls.getName()); return "void*";
} else if (cls == String.class) { } else if (cls == String.class) {
return "JavaObject*"; return "JavaObject*";
} else { } else {
@ -166,12 +194,88 @@ public class MetadataCIntrinsic implements Intrinsic {
} }
} }
static class MethodContext { private void writeResourceArray(IntrinsicContext context, ResourceArray<?> resourceArray) {
String baseName; staticFieldInitWriter.println("&(struct { int32_t size; void* data[" + resourceArray.size() + "]; }) {")
int suffix; .indent();
staticFieldInitWriter.println(".size = " + resourceArray.size() + ",");
staticFieldInitWriter.print(".data = {").indent();
MethodContext(String baseName) { boolean first = true;
this.baseName = baseName; for (int i = 0; i < resourceArray.size(); ++i) {
if (!first) {
staticFieldInitWriter.print(",");
}
staticFieldInitWriter.println();
first = false;
writeValue(context, resourceArray.get(i));
} }
staticFieldInitWriter.println().outdent().println("}");
staticFieldInitWriter.outdent().print("}");
}
private void writeResourceMap(IntrinsicContext context, ResourceMap<?> resourceMap) {
String[] keys = resourceMap.keys();
int tableSize = keys.length * 2;
int maxTableSize = Math.min(keys.length * 5 / 2, tableSize + 10);
String[] bestTable = null;
int bestCollisionRatio = 0;
while (tableSize <= maxTableSize) {
String[] table = new String[tableSize];
int maxCollisionRatio = 0;
for (String key : keys) {
int hashCode = key.hashCode();
int collisionRatio = 0;
while (true) {
int index = mod(hashCode++, table.length);
if (table[index] == null) {
table[index] = key;
break;
}
collisionRatio++;
}
maxCollisionRatio = Math.max(maxCollisionRatio, collisionRatio);
}
if (bestTable == null || bestCollisionRatio > maxCollisionRatio) {
bestCollisionRatio = maxCollisionRatio;
bestTable = table;
}
tableSize++;
}
staticFieldInitWriter.println("&(struct { int32_t size; TeaVM_ResourceMapEntry entries["
+ bestTable.length + "]; }) {").indent();
staticFieldInitWriter.println(".size = " + bestTable.length + ",");
staticFieldInitWriter.print(".entries = {").indent();
boolean first = true;
for (String key : bestTable) {
if (!first) {
staticFieldInitWriter.print(",");
}
staticFieldInitWriter.println();
first = false;
if (key == null) {
staticFieldInitWriter.print("{ NULL, NULL }");
} else {
staticFieldInitWriter.print("{ stringPool + " + context.getStringPool().getStringIndex(key) + ", ");
writeValue(context, resourceMap.get(key));
staticFieldInitWriter.print("}");
}
}
staticFieldInitWriter.println().outdent().println("}");
staticFieldInitWriter.outdent().print("}");
}
private static int mod(int a, int b) {
a %= b;
if (a < 0) {
a += b;
}
return a;
} }
} }

View File

@ -21,6 +21,8 @@ import org.teavm.backend.c.intrinsic.IntrinsicContext;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.Resource; import org.teavm.platform.metadata.Resource;
import org.teavm.platform.metadata.ResourceArray;
import org.teavm.platform.metadata.ResourceMap;
public class ResourceReadCIntrinsic implements Intrinsic { public class ResourceReadCIntrinsic implements Intrinsic {
private ClassReaderSource classSource; private ClassReaderSource classSource;
@ -36,6 +38,14 @@ public class ResourceReadCIntrinsic implements Intrinsic {
@Override @Override
public void apply(IntrinsicContext context, InvocationExpr invocation) { public void apply(IntrinsicContext context, InvocationExpr invocation) {
if (invocation.getMethod().getClassName().equals(ResourceMap.class.getName())) {
applyForResourceMap(context, invocation);
return;
} else if (invocation.getMethod().getClassName().equals(ResourceArray.class.getName())) {
applyForResourceArray(context, invocation);
return;
}
String name = invocation.getMethod().getName(); String name = invocation.getMethod().getName();
if (name.startsWith("get")) { if (name.startsWith("get")) {
name = name.substring(3); name = name.substring(3);
@ -52,4 +62,45 @@ public class ResourceReadCIntrinsic implements Intrinsic {
context.writer().print(", ").print(context.names().forClass(invocation.getMethod().getClassName())); context.writer().print(", ").print(context.names().forClass(invocation.getMethod().getClassName()));
context.writer().print(", ").print(name).print(")"); context.writer().print(", ").print(name).print(")");
} }
private void applyForResourceMap(IntrinsicContext context, InvocationExpr invocation) {
switch (invocation.getMethod().getName()) {
case "keys":
context.writer().print("teavm_resourceMapKeys((TeaVM_ResourceMap*) ");
context.emit(invocation.getArguments().get(0));
context.writer().print(")");
break;
case "has":
context.writer().print("(teavm_lookupResource((TeaVM_ResourceMap*) ");
context.emit(invocation.getArguments().get(0));
context.writer().print(", (JavaString*) ");
context.emit(invocation.getArguments().get(1));
context.writer().print(") != NULL)");
break;
case "get":
context.writer().print("teavm_lookupResourceValue((TeaVM_ResourceMap*) ");
context.emit(invocation.getArguments().get(0));
context.writer().print(", (JavaString*) ");
context.emit(invocation.getArguments().get(1));
context.writer().print(")");
break;
}
}
private void applyForResourceArray(IntrinsicContext context, InvocationExpr invocation) {
switch (invocation.getMethod().getName()) {
case "size":
context.writer().print("((TeaVM_ResourceArray*) ");
context.emit(invocation.getArguments().get(0));
context.writer().print(")->size");
break;
case "get":
context.writer().print("((TeaVM_ResourceArray*) ");
context.emit(invocation.getArguments().get(0));
context.writer().print(")->data[");
context.emit(invocation.getArguments().get(1));
context.writer().print("]");
break;
}
}
} }