Add emitter. Refactor metadata generator. Fix bugs in timezone

formatting and parsing
This commit is contained in:
Alexey Andreev 2015-05-22 18:18:53 +04:00
parent bfd97bcae7
commit e7a88d087c
9 changed files with 684 additions and 30 deletions

View File

@ -230,8 +230,8 @@ public abstract class TDateFormat extends TFormat {
public TDate parse(String string) throws TParseException { public TDate parse(String string) throws TParseException {
TParsePosition position = new TParsePosition(0); TParsePosition position = new TParsePosition(0);
TDate date = parse(string, position); TDate date = parse(string, position);
if (position.getIndex() == 0) { if (position.getErrorIndex() > 0) {
throw new TParseException("Unparseable date" + string, position.getErrorIndex()); throw new TParseException("Unparseable date: " + string, position.getErrorIndex());
} }
return date; return date;
} }

View File

@ -400,6 +400,7 @@ abstract class TDateFormatElement {
if (position.getIndex() + 1 < text.length()) { if (position.getIndex() + 1 < text.length()) {
} }
prepareTrie();
TTimeZone tz = match(searchTrie, text, position); TTimeZone tz = match(searchTrie, text, position);
if (tz != null) { if (tz != null) {
date.setTimeZone(tz); date.setTimeZone(tz);
@ -415,12 +416,11 @@ abstract class TDateFormatElement {
} }
public TTimeZone match(TrieNode node, String text, TParsePosition position) { public TTimeZone match(TrieNode node, String text, TParsePosition position) {
prepareTrie();
int start = position.getIndex(); int start = position.getIndex();
int index = start; int index = start;
int lastMatch = start; int lastMatch = start;
TTimeZone tz = null; TTimeZone tz = null;
while (node.childNodes.length > 0) { while (node.childNodes != null && node.childNodes.length > 0) {
if (node.tz != null) { if (node.tz != null) {
lastMatch = index; lastMatch = index;
tz = node.tz; tz = node.tz;
@ -432,7 +432,11 @@ abstract class TDateFormatElement {
if (next < 0) { if (next < 0) {
return null; return null;
} }
node = node.childNodes[index]; node = node.childNodes[next];
}
if (node.tz != null) {
lastMatch = index;
tz = node.tz;
} }
position.setIndex(lastMatch); position.setIndex(lastMatch);
return tz; return tz;
@ -459,6 +463,7 @@ abstract class TDateFormatElement {
TTimeZone tz = TTimeZone.getTimeZone(tzId); TTimeZone tz = TTimeZone.getTimeZone(tzId);
builder.add(tz.getID(), tz); builder.add(tz.getID(), tz);
} }
idSearchTrie = builder.build();
} }
} }
@ -485,6 +490,7 @@ abstract class TDateFormatElement {
position.setErrorIndex(index); position.setErrorIndex(index);
return; return;
} }
++index;
if (index + 2 > text.length() || !Character.isDigit(text.charAt(index)) || if (index + 2 > text.length() || !Character.isDigit(text.charAt(index)) ||
!Character.isDigit(text.charAt(index + 1))) { !Character.isDigit(text.charAt(index + 1))) {
@ -511,10 +517,9 @@ abstract class TDateFormatElement {
TrieNodeBuilder root = new TrieNodeBuilder(); TrieNodeBuilder root = new TrieNodeBuilder();
public void add(String text, TTimeZone tz) { public void add(String text, TTimeZone tz) {
int index = 0;
TrieNodeBuilder node = root; TrieNodeBuilder node = root;
while (index < text.length()) { for (int i = 0; i < text.length(); ++i) {
char c = Character.toLowerCase(text.charAt(index)); char c = Character.toLowerCase(text.charAt(i));
while (node.ch != c) { while (node.ch != c) {
if (node.ch == '\0') { if (node.ch == '\0') {
node.ch = c; node.ch = c;
@ -537,12 +542,15 @@ abstract class TDateFormatElement {
TrieNode build(TrieNodeBuilder builder) { TrieNode build(TrieNodeBuilder builder) {
TrieNode node = new TrieNode(); TrieNode node = new TrieNode();
if (builder == null) {
return node;
}
node.tz = builder.tz; node.tz = builder.tz;
List<TrieNodeBuilder> builders = new ArrayList<>(); List<TrieNodeBuilder> builders = new ArrayList<>();
TrieNodeBuilder tmp = builder; TrieNodeBuilder tmp = builder;
while (tmp.ch != '\0') { while (tmp.ch != '\0') {
builders.add(builder); builders.add(tmp);
builder = builder.sibling; tmp = tmp.sibling;
} }
Collections.sort(builders, new Comparator<TrieNodeBuilder>() { Collections.sort(builders, new Comparator<TrieNodeBuilder>() {
@Override public int compare(TrieNodeBuilder o1, TrieNodeBuilder o2) { @Override public int compare(TrieNodeBuilder o1, TrieNodeBuilder o2) {
@ -553,7 +561,7 @@ abstract class TDateFormatElement {
node.childNodes = new TrieNode[builders.size()]; node.childNodes = new TrieNode[builders.size()];
for (int i = 0; i < node.chars.length; ++i) { for (int i = 0; i < node.chars.length; ++i) {
node.chars[i] = builders.get(i).ch; node.chars[i] = builders.get(i).ch;
node.childNodes[i] = build(builders.get(i)); node.childNodes[i] = build(builders.get(i).next);
} }
return node; return node;
} }

View File

@ -139,6 +139,12 @@ public class MethodReference {
return reprCache; return reprCache;
} }
public static MethodReference parse(String string) {
int index = string.lastIndexOf('.');
String className = string.substring(0, index);
return new MethodReference(className, MethodDescriptor.parse(string.substring(index + 1)));
}
public String signatureToString() { public String signatureToString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append('('); sb.append('(');

View File

@ -0,0 +1,65 @@
/*
* 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.model.emit;
import org.teavm.model.BasicBlock;
/**
*
* @author Alexey Andreev
*/
public abstract class ForkEmitter {
public abstract void setThen(BasicBlock block);
public abstract void setElse(BasicBlock block);
public ForkEmitter and(BasicBlock block, final ForkEmitter other) {
setThen(block);
return new ForkEmitter() {
@Override public void setThen(BasicBlock block) {
other.setThen(block);
}
@Override public void setElse(BasicBlock block) {
ForkEmitter.this.setElse(block);
other.setElse(block);
}
};
}
public ForkEmitter or(BasicBlock block, final ForkEmitter other) {
setElse(block);
return new ForkEmitter() {
@Override public void setThen(BasicBlock block) {
ForkEmitter.this.setThen(block);
other.setThen(block);
}
@Override public void setElse(BasicBlock block) {
other.setElse(block);
}
};
}
public ForkEmitter not() {
return new ForkEmitter() {
@Override public void setThen(BasicBlock block) {
ForkEmitter.this.setElse(block);
}
@Override public void setElse(BasicBlock block) {
ForkEmitter.this.setThen(block);
}
};
}
}

View File

@ -0,0 +1,217 @@
/*
* 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.model.emit;
import org.teavm.model.*;
import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.instructions.LongConstantInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.instructions.StringConstantInstruction;
/**
*
* @author Alexey Andreev
*/
public final class ProgramEmitter {
private Program program;
private BasicBlock block;
private InstructionLocation currentLocation;
private ProgramEmitter(Program program, BasicBlock block) {
this.program = program;
this.block = block;
}
public Program getProgram() {
return program;
}
public BasicBlock getBlock() {
return block;
}
public void setBlock(BasicBlock block) {
this.block = block;
}
public BasicBlock createBlock() {
BasicBlock block = program.createBasicBlock();
setBlock(block);
return block;
}
public ValueEmitter constant(String value) {
Variable var = program.createVariable();
StringConstantInstruction insn = new StringConstantInstruction();
insn.setReceiver(var);
insn.setConstant(value);
addInstruction(insn);
return wrap(var);
}
public ValueEmitter constant(int value) {
Variable var = program.createVariable();
IntegerConstantInstruction insn = new IntegerConstantInstruction();
insn.setReceiver(var);
insn.setConstant(value);
addInstruction(insn);
return wrap(var);
}
public ValueEmitter constant(long value) {
Variable var = program.createVariable();
LongConstantInstruction insn = new LongConstantInstruction();
insn.setReceiver(var);
insn.setConstant(value);
addInstruction(insn);
return wrap(var);
}
public ValueEmitter constant(float value) {
Variable var = program.createVariable();
FloatConstantInstruction insn = new FloatConstantInstruction();
insn.setReceiver(var);
insn.setConstant(value);
addInstruction(insn);
return wrap(var);
}
public ValueEmitter constant(double value) {
Variable var = program.createVariable();
DoubleConstantInstruction insn = new DoubleConstantInstruction();
insn.setReceiver(var);
insn.setConstant(value);
addInstruction(insn);
return wrap(var);
}
public ValueEmitter constantNull() {
Variable var = program.createVariable();
NullConstantInstruction insn = new NullConstantInstruction();
insn.setReceiver(var);
addInstruction(insn);
return wrap(var);
}
public ValueEmitter getField(FieldReference field, ValueType type) {
Variable var = program.createVariable();
GetFieldInstruction insn = new GetFieldInstruction();
insn.setField(field);
insn.setFieldType(type);
insn.setReceiver(var);
addInstruction(insn);
return wrap(var);
}
public ProgramEmitter setField(FieldReference field, ValueType type, ValueEmitter value) {
PutFieldInstruction insn = new PutFieldInstruction();
insn.setField(field);
insn.setFieldType(type);
insn.setValue(value.getVariable());
addInstruction(insn);
return this;
}
public ValueEmitter invoke(MethodReference method, ValueEmitter... arguments) {
Variable result = null;
if (method.getReturnType() != ValueType.VOID) {
result = program.createVariable();
}
InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL);
insn.setMethod(method);
insn.setReceiver(result);
for (ValueEmitter arg : arguments) {
insn.getArguments().add(arg.variable);
}
addInstruction(insn);
return result != null ? wrap(result) : null;
}
public ProgramEmitter invokeAndIgnore(MethodReference method, ValueEmitter... arguments) {
invoke(method, arguments);
return this;
}
public ValueEmitter construct(MethodReference method, ValueEmitter... arguments) {
Variable var = program.createVariable();
ConstructInstruction insn = new ConstructInstruction();
insn.setReceiver(var);
insn.setType(method.getClassName());
addInstruction(insn);
ValueEmitter instance = wrap(var);
instance.invokeSpecial(method, arguments);
return instance;
}
public ProgramEmitter jump(BasicBlock block) {
JumpInstruction insn = new JumpInstruction();
insn.setTarget(block);
addInstruction(insn);
this.block = block;
return this;
}
public void exit() {
ExitInstruction insn = new ExitInstruction();
addInstruction(insn);
}
public ValueEmitter wrap(Variable var) {
return new ValueEmitter(this, block, var);
}
public ValueEmitter wrapNew() {
return wrap(program.createVariable());
}
public InstructionLocation getCurrentLocation() {
return currentLocation;
}
public void setCurrentLocation(InstructionLocation currentLocation) {
this.currentLocation = currentLocation;
}
public void addInstruction(Instruction insn) {
if (currentLocation != null) {
insn.setLocation(currentLocation);
}
block.getInstructions().add(insn);
}
public static ProgramEmitter create(MethodHolder method) {
Program program = new Program();
method.setProgram(program);
BasicBlock zeroBlock = program.createBasicBlock();
BasicBlock block = program.createBasicBlock();
JumpInstruction insn = new JumpInstruction();
insn.setTarget(block);
zeroBlock.getInstructions().add(insn);
return new ProgramEmitter(program, block);
}
}

View File

@ -0,0 +1,280 @@
/*
* 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.model.emit;
import org.teavm.model.BasicBlock;
import org.teavm.model.FieldReference;
import org.teavm.model.Incoming;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.ArrayLengthInstruction;
import org.teavm.model.instructions.BinaryBranchingCondition;
import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BinaryInstruction;
import org.teavm.model.instructions.BinaryOperation;
import org.teavm.model.instructions.BranchingCondition;
import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.CastIntegerDirection;
import org.teavm.model.instructions.CastIntegerInstruction;
import org.teavm.model.instructions.CastNumberInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.GetElementInstruction;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.IntegerSubtype;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.IsInstanceInstruction;
import org.teavm.model.instructions.NegateInstruction;
import org.teavm.model.instructions.NumericOperandType;
import org.teavm.model.instructions.PutFieldInstruction;
/**
*
* @author Alexey Andreev
*/
public class ValueEmitter {
ProgramEmitter pe;
BasicBlock block;
Variable variable;
ValueEmitter(ProgramEmitter programEmitter, BasicBlock block, Variable variable) {
this.pe = programEmitter;
this.block = block;
this.variable = variable;
}
public ProgramEmitter getProgramEmitter() {
return pe;
}
public BasicBlock getBlock() {
return block;
}
public Variable getVariable() {
return variable;
}
public ValueEmitter getField(FieldReference field, ValueType type) {
Variable var = pe.getProgram().createVariable();
GetFieldInstruction insn = new GetFieldInstruction();
insn.setField(field);
insn.setFieldType(type);
insn.setReceiver(var);
insn.setInstance(variable);
pe.addInstruction(insn);
return pe.wrap(var);
}
public void setField(FieldReference field, ValueType type, ValueEmitter value) {
PutFieldInstruction insn = new PutFieldInstruction();
insn.setField(field);
insn.setFieldType(type);
insn.setInstance(variable);
insn.setValue(value.getVariable());
pe.addInstruction(insn);
}
public ValueEmitter binary(BinaryOperation op, NumericOperandType type, ValueEmitter other) {
Variable var = pe.getProgram().createVariable();
BinaryInstruction insn = new BinaryInstruction(op, type);
insn.setFirstOperand(variable);
insn.setSecondOperand(other.variable);
insn.setReceiver(var);
pe.addInstruction(insn);
return pe.wrap(var);
}
public ValueEmitter add(NumericOperandType type, ValueEmitter other) {
return binary(BinaryOperation.ADD, type, other);
}
public ValueEmitter iadd(ValueEmitter other) {
return add(NumericOperandType.INT, other);
}
public ValueEmitter sub(NumericOperandType type, ValueEmitter other) {
return binary(BinaryOperation.SUBTRACT, type, other);
}
public ValueEmitter isub(ValueEmitter other) {
return sub(NumericOperandType.INT, other);
}
public ValueEmitter compare(NumericOperandType type, ValueEmitter other) {
return binary(BinaryOperation.COMPARE, type, other);
}
public ValueEmitter icompare(ValueEmitter other) {
return compare(NumericOperandType.INT, other);
}
public ValueEmitter neg(NumericOperandType type) {
Variable var = pe.getProgram().createVariable();
NegateInstruction insn = new NegateInstruction(type);
insn.setOperand(variable);
insn.setReceiver(var);
return pe.wrap(var);
}
public ValueEmitter ineg() {
return neg(NumericOperandType.INT);
}
public ValueEmitter invoke(InvocationType type, MethodReference method, ValueEmitter... arguments) {
Variable result = null;
if (method.getReturnType() != ValueType.VOID) {
result = pe.getProgram().createVariable();
}
InvokeInstruction insn = new InvokeInstruction();
insn.setType(type);
insn.setMethod(method);
insn.setInstance(variable);
insn.setReceiver(result);
for (ValueEmitter arg : arguments) {
insn.getArguments().add(arg.variable);
}
pe.addInstruction(insn);
return result != null ? pe.wrap(result) : null;
}
public ValueEmitter invokeSpecial(MethodReference method, ValueEmitter... arguments) {
return invoke(InvocationType.SPECIAL, method, arguments);
}
public ValueEmitter invokeVirtual(MethodReference method, ValueEmitter... arguments) {
return invoke(InvocationType.VIRTUAL, method, arguments);
}
public ValueEmitter join(ValueEmitter other) {
Variable var = pe.getProgram().createVariable();
Phi phi = new Phi();
phi.setReceiver(var);
Incoming incoming = new Incoming();
incoming.setSource(block);
incoming.setValue(variable);
phi.getIncomings().add(incoming);
incoming = new Incoming();
incoming.setSource(other.block);
incoming.setValue(other.variable);
phi.getIncomings().add(incoming);
pe.getBlock().getPhis().add(phi);
return new ValueEmitter(pe, pe.getBlock(), var);
}
public ForkEmitter fork(BinaryBranchingCondition condition, ValueEmitter other) {
final BinaryBranchingInstruction insn = new BinaryBranchingInstruction(condition);
insn.setFirstOperand(variable);
insn.setSecondOperand(other.variable);
pe.addInstruction(insn);
return new ForkEmitter() {
@Override public void setThen(BasicBlock block) {
insn.setConsequent(block);
}
@Override public void setElse(BasicBlock block) {
insn.setAlternative(block);
}
};
}
public ForkEmitter fork(BranchingCondition condition) {
final BranchingInstruction insn = new BranchingInstruction(condition);
insn.setOperand(variable);
pe.addInstruction(insn);
return new ForkEmitter() {
@Override public void setThen(BasicBlock block) {
insn.setConsequent(block);
}
@Override public void setElse(BasicBlock block) {
insn.setAlternative(block);
}
};
}
public void returnValue() {
ExitInstruction insn = new ExitInstruction();
insn.setValueToReturn(variable);
pe.addInstruction(insn);
}
public ValueEmitter cast(ValueType type) {
Variable result = pe.getProgram().createVariable();
CastInstruction insn = new CastInstruction();
insn.setValue(variable);
insn.setReceiver(result);
insn.setTargetType(type);
pe.addInstruction(insn);
return pe.wrap(result);
}
public ValueEmitter cast(NumericOperandType from, NumericOperandType to) {
Variable result = pe.getProgram().createVariable();
CastNumberInstruction insn = new CastNumberInstruction(from, to);
insn.setValue(variable);
insn.setReceiver(result);
pe.addInstruction(insn);
return pe.wrap(result);
}
public ValueEmitter cast(IntegerSubtype subtype, CastIntegerDirection dir) {
Variable result = pe.getProgram().createVariable();
CastIntegerInstruction insn = new CastIntegerInstruction(subtype, dir);
insn.setValue(variable);
insn.setReceiver(result);
pe.addInstruction(insn);
return pe.wrap(result);
}
public ValueEmitter toInteger(IntegerSubtype from) {
return cast(from, CastIntegerDirection.TO_INTEGER);
}
public ValueEmitter fromInteger(IntegerSubtype to) {
return cast(to, CastIntegerDirection.FROM_INTEGER);
}
public ValueEmitter getElement(ValueEmitter index) {
Variable result = pe.getProgram().createVariable();
GetElementInstruction insn = new GetElementInstruction();
insn.setArray(variable);
insn.setIndex(index.variable);
insn.setReceiver(variable);
pe.addInstruction(insn);
return pe.wrap(result);
}
public ValueEmitter arrayLength() {
Variable result = pe.getProgram().createVariable();
ArrayLengthInstruction insn = new ArrayLengthInstruction();
insn.setArray(variable);
insn.setReceiver(result);
pe.addInstruction(insn);
return pe.wrap(result);
}
public ValueEmitter instanceOf(ValueType type) {
Variable result = pe.getProgram().createVariable();
IsInstanceInstruction insn = new IsInstanceInstruction();
insn.setValue(variable);
insn.setReceiver(result);
insn.setType(type);
pe.addInstruction(insn);
return pe.wrap(result);
}
}

View File

@ -33,18 +33,12 @@ import org.teavm.platform.metadata.Resource;
public class MetadataProviderNativeGenerator implements Generator { public class MetadataProviderNativeGenerator implements Generator {
@Override @Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
// Validate method
ClassReader cls = context.getClassSource().get(methodRef.getClassName()); ClassReader cls = context.getClassSource().get(methodRef.getClassName());
MethodReader method = cls.getMethod(methodRef.getDescriptor()); MethodReader method = cls.getMethod(methodRef.getDescriptor());
AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName()); AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName());
if (providerAnnot == null) {
return; AnnotationReader refAnnot = method.getAnnotations().get(MetadataProviderRef.class.getName());
} methodRef = MethodReference.parse(refAnnot.getValue("value").getString());
if (!method.hasModifier(ElementModifier.NATIVE)) {
context.getDiagnostics().error(new CallLocation(methodRef), "Method {{m0}} is marked with " +
"{{c1}} annotation, but it is not native", methodRef, MetadataProvider.class.getName());
return;
}
// Find and instantiate metadata generator // Find and instantiate metadata generator
ValueType generatorType = providerAnnot.getValue("value").getJavaClass(); ValueType generatorType = providerAnnot.getValue("value").getJavaClass();
@ -78,12 +72,8 @@ public class MetadataProviderNativeGenerator implements Generator {
// Generate resource loader // Generate resource loader
Resource resource = generator.generateMetadata(metadataContext, methodRef); Resource resource = generator.generateMetadata(metadataContext, methodRef);
writer.append("if (!window.hasOwnProperty(\"").appendMethodBody(methodRef).append("$$resource\")) {") writer.append("return ");
.indent().softNewLine();
writer.append("window.").appendMethodBody(methodRef).append("$$resource = ");
ResourceWriterHelper.write(writer, resource); ResourceWriterHelper.write(writer, resource);
writer.append(';').softNewLine(); writer.append(';').softNewLine();
writer.outdent().append('}').softNewLine();
writer.append("return ").appendMethodBody(methodRef).append("$$resource;").softNewLine();
} }
} }

View File

@ -0,0 +1,31 @@
/*
* 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.platform.plugin;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author Alexey Andreev
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
@interface MetadataProviderRef {
String value();
}

View File

@ -18,6 +18,10 @@ package org.teavm.platform.plugin;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.javascript.spi.GeneratedBy; import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.emit.ForkEmitter;
import org.teavm.model.emit.ProgramEmitter;
import org.teavm.model.instructions.BinaryBranchingCondition;
import org.teavm.model.util.ModelUtils;
import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformClass;
import org.teavm.platform.metadata.ClassScopedMetadataProvider; import org.teavm.platform.metadata.ClassScopedMetadataProvider;
import org.teavm.platform.metadata.MetadataProvider; import org.teavm.platform.metadata.MetadataProvider;
@ -27,15 +31,14 @@ import org.teavm.platform.metadata.MetadataProvider;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
class MetadataProviderTransformer implements ClassHolderTransformer { class MetadataProviderTransformer implements ClassHolderTransformer {
static int fieldIdGen;
@Override @Override
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
for (MethodHolder method : cls.getMethods()) { for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName()); AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName());
if (providerAnnot != null) { if (providerAnnot != null) {
AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName()); transformMetadataMethod(cls, method, diagnostics);
genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
MetadataProviderNativeGenerator.class.getName())));
method.getAnnotations().add(genAnnot);
} }
providerAnnot = method.getAnnotations().get(ClassScopedMetadataProvider.class.getName()); providerAnnot = method.getAnnotations().get(ClassScopedMetadataProvider.class.getName());
if (providerAnnot != null) { if (providerAnnot != null) {
@ -53,4 +56,58 @@ class MetadataProviderTransformer implements ClassHolderTransformer {
} }
} }
} }
private void transformMetadataMethod(ClassHolder cls, MethodHolder method, Diagnostics diagnostics) {
if (!validate(method, diagnostics)) {
return;
}
FieldHolder field = new FieldHolder("$$metadata$$" + fieldIdGen++);
field.setType(method.getResultType());
field.setLevel(AccessLevel.PRIVATE);
field.getModifiers().add(ElementModifier.STATIC);
cls.addField(field);
MethodHolder createMethod = new MethodHolder(method.getName() + "$$create", method.getSignature());
createMethod.setLevel(AccessLevel.PRIVATE);
createMethod.getModifiers().add(ElementModifier.NATIVE);
createMethod.getModifiers().add(ElementModifier.STATIC);
cls.addMethod(createMethod);
AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName());
genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
MetadataProviderNativeGenerator.class.getName())));
createMethod.getAnnotations().add(genAnnot);
ModelUtils.copyAnnotations(method.getAnnotations(), createMethod.getAnnotations());
AnnotationHolder refAnnot = new AnnotationHolder(MetadataProviderRef.class.getName());
refAnnot.getValues().put("value", new AnnotationValue(method.getReference().toString()));
createMethod.getAnnotations().add(refAnnot);
method.getModifiers().remove(ElementModifier.NATIVE);
ProgramEmitter pe = ProgramEmitter.create(method);
ForkEmitter fork = pe.getField(field.getReference(), field.getType()).fork(
BinaryBranchingCondition.REFERENCE_NOT_EQUAL, pe.constantNull());
BasicBlock resourceFound = pe.createBlock();
fork.setThen(resourceFound);
pe.getField(field.getReference(), field.getType()).returnValue();
fork.setElse(pe.createBlock());
pe.setField(field.getReference(), field.getType(), pe.invoke(createMethod.getReference()));
pe.jump(resourceFound);
}
private boolean validate(MethodHolder method, Diagnostics diagnostics) {
AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName());
if (providerAnnot == null) {
return false;
}
if (!method.hasModifier(ElementModifier.NATIVE)) {
diagnostics.error(new CallLocation(method.getReference()), "Method {{m0}} is marked with " +
"{{c1}} annotation, but it is not native", method.getReference(),
MetadataProvider.class.getName());
return false;
}
return true;
}
} }