mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
C backend: implement support for ResourceArray and ResourceMap
This commit is contained in:
parent
401a9ed212
commit
f532801f38
|
@ -19,9 +19,11 @@ import java.lang.reflect.Array;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import org.teavm.backend.c.TeaVMCHost;
|
||||
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
|
||||
import org.teavm.classlib.ReflectionSupplier;
|
||||
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.java.lang.reflect.AnnotationDependencyListener;
|
||||
import org.teavm.interop.PlatformMarker;
|
||||
|
@ -85,6 +87,11 @@ public class JCLPlugin implements TeaVMPlugin {
|
|||
host.add(reflection);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -353,6 +353,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
|
||||
private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
|
||||
generateThrowCCE(context, writer);
|
||||
generateAllocateStringArray(context, writer);
|
||||
}
|
||||
|
||||
private void generateThrowCCE(GenerationContext context, CodeWriter writer) {
|
||||
|
@ -364,6 +365,16 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
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,
|
||||
Set<? extends ValueType> types) {
|
||||
writer.println("int main(int argc, char** argv) {").indent();
|
||||
|
|
|
@ -34,4 +34,6 @@ public interface IntrinsicContext {
|
|||
MethodReference getCallingMethod();
|
||||
|
||||
StringPool getStringPool();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,3 +5,49 @@ static inline int32_t instanceof(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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
|
@ -114,6 +114,46 @@ static inline float TeaVM_getNaN() {
|
|||
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() {
|
||||
srand(time(NULL));
|
||||
}
|
||||
|
@ -158,6 +198,13 @@ static int64_t currentTimeMillis() {
|
|||
|
||||
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
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -30,6 +30,8 @@ import org.teavm.model.MethodReference;
|
|||
import org.teavm.platform.metadata.MetadataGenerator;
|
||||
import org.teavm.platform.metadata.MetadataProvider;
|
||||
import org.teavm.platform.metadata.Resource;
|
||||
import org.teavm.platform.metadata.ResourceArray;
|
||||
import org.teavm.platform.metadata.ResourceMap;
|
||||
|
||||
public class MetadataCIntrinsic implements Intrinsic {
|
||||
private ClassReaderSource classSource;
|
||||
|
@ -89,11 +91,36 @@ public class MetadataCIntrinsic implements Intrinsic {
|
|||
}
|
||||
|
||||
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);
|
||||
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) {
|
||||
writeResource(context, (ResourceTypeDescriptorProvider) value);
|
||||
} else if (value instanceof ResourceMap) {
|
||||
writeResourceMap(context, (ResourceMap<?>) value);
|
||||
} else if (value instanceof ResourceArray) {
|
||||
writeResourceArray(context, (ResourceArray<?>) value);
|
||||
} else {
|
||||
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()) {
|
||||
if (Resource.class.isAssignableFrom(propertyType)) {
|
||||
if (Resource.class.isAssignableFrom(propertyType) && !ResourceMap.class.isAssignableFrom(propertyType)
|
||||
&& !ResourceArray.class.isAssignableFrom(propertyType)) {
|
||||
ResourceTypeDescriptor propertyStructure = metadataContext.getTypeDescriptor(
|
||||
propertyType.asSubclass(Resource.class));
|
||||
writeResourceStructure(context, propertyStructure);
|
||||
|
@ -158,7 +186,7 @@ public class MetadataCIntrinsic implements Intrinsic {
|
|||
} else if (cls == double.class) {
|
||||
return "double";
|
||||
} else if (Resource.class.isAssignableFrom(cls)) {
|
||||
return "&" + context.names().forClass(cls.getName());
|
||||
return "void*";
|
||||
} else if (cls == String.class) {
|
||||
return "JavaObject*";
|
||||
} else {
|
||||
|
@ -166,12 +194,88 @@ public class MetadataCIntrinsic implements Intrinsic {
|
|||
}
|
||||
}
|
||||
|
||||
static class MethodContext {
|
||||
String baseName;
|
||||
int suffix;
|
||||
private void writeResourceArray(IntrinsicContext context, ResourceArray<?> resourceArray) {
|
||||
staticFieldInitWriter.println("&(struct { int32_t size; void* data[" + resourceArray.size() + "]; }) {")
|
||||
.indent();
|
||||
staticFieldInitWriter.println(".size = " + resourceArray.size() + ",");
|
||||
staticFieldInitWriter.print(".data = {").indent();
|
||||
|
||||
MethodContext(String baseName) {
|
||||
this.baseName = baseName;
|
||||
boolean first = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ import org.teavm.backend.c.intrinsic.IntrinsicContext;
|
|||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.platform.metadata.Resource;
|
||||
import org.teavm.platform.metadata.ResourceArray;
|
||||
import org.teavm.platform.metadata.ResourceMap;
|
||||
|
||||
public class ResourceReadCIntrinsic implements Intrinsic {
|
||||
private ClassReaderSource classSource;
|
||||
|
@ -36,6 +38,14 @@ public class ResourceReadCIntrinsic implements Intrinsic {
|
|||
|
||||
@Override
|
||||
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();
|
||||
if (name.startsWith("get")) {
|
||||
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(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user