Adds more metadata usecases

This commit is contained in:
konsoletyper 2014-06-09 19:44:36 +04:00
parent b9d6e29ca2
commit da2a26fb4f
5 changed files with 147 additions and 7 deletions

View File

@ -61,5 +61,44 @@ public class MetadataGeneratorTest {
assertEquals("baz", res.getArrayB().get(0).getBar());
assertNull(res.getArrayC());
}
@MetadataProvider(TestResourceGenerator.class)
private native TestResource getEmptyResource();
@Test
public void resourceDefaultsSet() {
TestResource res = getEmptyResource();
assertEquals(0, res.getA());
assertFalse(res.getB());
assertEquals(0, res.getD());
assertEquals(0, res.getE());
assertEquals(0, res.getF(), 1E-10);
assertEquals(0, res.getG(), 1E-10);
assertNull(res.getFoo());
assertNull(res.getArrayA());
assertNull(res.getArrayB());
assertNull(res.getArrayC());
assertNull(res.getMapA());
assertNull(res.getMapB());
assertNull(res.getMapC());
}
@Test
public void resourceModifiedInRunTime() {
TestResource res = getEmptyResource();
res.setA(23);
res.setB(true);
res.setD((byte)24);
res.setE((short)25);
res.setF(3.14f);
res.setG(2.72);
assertEquals(23, res.getA());
assertTrue(res.getB());
assertEquals(24, res.getD());
assertEquals(25, res.getE());
assertEquals(3.14, res.getF(), 0.001);
assertEquals(2.72, res.getG(), 0.001);
}
}

View File

@ -31,6 +31,8 @@ public class TestResourceGenerator implements MetadataGenerator {
return createInt(context, 23);
case "getResource":
return getResource(context);
case "getEmptyResource":
return context.createResource(TestResource.class);
default:
throw new RuntimeException("Unsupported method: " + method);
}

View File

@ -62,6 +62,7 @@ class BuildTimeResourceProxyBuilder {
Map<Method, BuildTimeResourceMethod> methods = new HashMap<>();
private Map<String, Integer> propertyIndexes = new HashMap<>();
private Object[] initialData;
private Map<String, Class<?>> propertyTypes = new HashMap<>();
public ProxyFactoryCreation(Class<?> iface) {
this.rootIface = iface;
@ -73,12 +74,24 @@ class BuildTimeResourceProxyBuilder {
" that is not an interface");
}
scanIface(rootIface);
// Fill default values
initialData = new Object[propertyIndexes.size()];
for (Map.Entry<String, Class<?>> property : propertyTypes.entrySet()) {
String propertyName = property.getKey();
Class<?> propertyType = property.getValue();
initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType);
}
// Generate write method
Method writeMethod;
try {
writeMethod = ResourceWriter.class.getMethod("write", SourceWriter.class);
} catch (NoSuchMethodException e) {
throw new AssertionError("Method must exist", e);
}
// Create factory
String[] properties = new String[propertyIndexes.size()];
for (Map.Entry<String, Integer> entry : propertyIndexes.entrySet()) {
properties[entry.getValue()] = entry.getKey();
@ -129,18 +142,19 @@ class BuildTimeResourceProxyBuilder {
}
}
// Verify types of properties and fill default values
initialData = new Object[propertyIndexes.size()];
// Verify types of properties
for (Map.Entry<String, Class<?>> property : getters.entrySet()) {
String propertyName = property.getKey();
Class<?> propertyType = property.getValue();
if (!allowedPropertyTypes.contains(propertyType)) {
if (!propertyType.isInterface() || !Resource.class.isAssignableFrom(propertyType)) {
throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName +
throw new IllegalArgumentException("Property " + rootIface.getName() + "." + propertyName +
" has an illegal type " + propertyType.getName());
}
}
initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType);
if (!propertyTypes.containsKey(propertyName)) {
propertyTypes.put(propertyName, propertyType);
}
}
// Scan superinterfaces

View File

@ -52,7 +52,7 @@ class ResourceAccessorGenerator implements Injector {
context.writeExpr(context.getArgument(0));
writePropertyAccessor(context, context.getArgument(1));
}
context.getWriter().append(']').ws().append('=').ws();
context.getWriter().ws().append('=').ws();
context.writeExpr(context.getArgument(2));
context.getWriter().append(')');
break;
@ -87,14 +87,20 @@ class ResourceAccessorGenerator implements Injector {
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(")");
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(")");
context.getWriter().append(")").ws().append(':').ws().append("null)");
break;
}
}

View File

@ -191,9 +191,88 @@ class ResourceProgramTransformer {
}
private List<Instruction> transformSetterInvocation(InvokeInstruction insn, String property) {
ValueType type = insn.getMethod().getDescriptor().parameterType(0);
List<Instruction> instructions = new ArrayList<>();
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive)type).getKind()) {
case BOOLEAN:
castAndSetProperty(insn, property, instructions, boolean.class);
return instructions;
case BYTE:
castAndSetProperty(insn, property, instructions, byte.class);
return instructions;
case SHORT:
castAndSetProperty(insn, property, instructions, short.class);
return instructions;
case INTEGER:
castAndSetProperty(insn, property, instructions, int.class);
return instructions;
case FLOAT:
castAndSetProperty(insn, property, instructions, float.class);
return instructions;
case DOUBLE:
castAndSetProperty(insn, property, instructions, double.class);
return instructions;
case CHARACTER:
case LONG:
break;
}
} else if (type instanceof ValueType.Object) {
switch (((ValueType.Object)type).getClassName()) {
case "java.lang.String": {
Variable castVar = insn.getProgram().createVariable();
InvokeInstruction castInvoke = new InvokeInstruction();
castInvoke.setType(InvocationType.SPECIAL);
castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castFromString",
String.class, Object.class));
castInvoke.getArguments().add(insn.getArguments().get(0));
castInvoke.setReceiver(castVar);
instructions.add(castInvoke);
setProperty(insn, property, instructions, castVar);
return instructions;
}
default: {
setProperty(insn, property, instructions, insn.getArguments().get(0));
return instructions;
}
}
}
return null;
}
private void setProperty(InvokeInstruction insn, String property, List<Instruction> instructions,
Variable valueVar) {
Variable nameVar = program.createVariable();
StringConstantInstruction nameInsn = new StringConstantInstruction();
nameInsn.setConstant(property);
nameInsn.setReceiver(nameVar);
instructions.add(nameInsn);
InvokeInstruction accessorInvoke = new InvokeInstruction();
accessorInvoke.setType(InvocationType.SPECIAL);
accessorInvoke.setMethod(new MethodReference(ResourceAccessor.class, "put",
Object.class, String.class, Object.class, void.class));
accessorInvoke.getArguments().add(insn.getInstance());
accessorInvoke.getArguments().add(nameVar);
accessorInvoke.getArguments().add(valueVar);
instructions.add(accessorInvoke);
}
private void castAndSetProperty(InvokeInstruction insn, String property, List<Instruction> instructions,
Class<?> primitive) {
Variable castVar = program.createVariable();
InvokeInstruction castInvoke = new InvokeInstruction();
castInvoke.setType(InvocationType.SPECIAL);
String primitiveCapitalized = primitive.getName();
primitiveCapitalized = Character.toUpperCase(primitiveCapitalized.charAt(0)) +
primitiveCapitalized.substring(1);
castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castFrom" + primitiveCapitalized,
primitive, Object.class));
castInvoke.getArguments().add(insn.getArguments().get(0));
castInvoke.setReceiver(castVar);
instructions.add(castInvoke);
setProperty(insn, property, instructions, castVar);
}
private String getPropertyName(String name) {
if (name.length() == 1) {
return name.toLowerCase();