From e7a88d087c99f088bb31415a1f0037096e6e5f00 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 22 May 2015 18:18:53 +0400 Subject: [PATCH] Add emitter. Refactor metadata generator. Fix bugs in timezone formatting and parsing --- .../teavm/classlib/java/text/TDateFormat.java | 4 +- .../java/text/TDateFormatElement.java | 26 +- .../java/org/teavm/model/MethodReference.java | 6 + .../org/teavm/model/emit/ForkEmitter.java | 65 ++++ .../org/teavm/model/emit/ProgramEmitter.java | 217 ++++++++++++++ .../org/teavm/model/emit/ValueEmitter.java | 280 ++++++++++++++++++ .../MetadataProviderNativeGenerator.java | 18 +- .../platform/plugin/MetadataProviderRef.java | 31 ++ .../plugin/MetadataProviderTransformer.java | 67 ++++- 9 files changed, 684 insertions(+), 30 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/model/emit/ForkEmitter.java create mode 100644 teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java create mode 100644 teavm-core/src/main/java/org/teavm/model/emit/ValueEmitter.java create mode 100644 teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderRef.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java index 280338b3e..dd1e607ae 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java @@ -230,8 +230,8 @@ public abstract class TDateFormat extends TFormat { public TDate parse(String string) throws TParseException { TParsePosition position = new TParsePosition(0); TDate date = parse(string, position); - if (position.getIndex() == 0) { - throw new TParseException("Unparseable date" + string, position.getErrorIndex()); + if (position.getErrorIndex() > 0) { + throw new TParseException("Unparseable date: " + string, position.getErrorIndex()); } return date; } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java index 6b4860f43..76bf8f7f7 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java @@ -400,6 +400,7 @@ abstract class TDateFormatElement { if (position.getIndex() + 1 < text.length()) { } + prepareTrie(); TTimeZone tz = match(searchTrie, text, position); if (tz != null) { date.setTimeZone(tz); @@ -415,12 +416,11 @@ abstract class TDateFormatElement { } public TTimeZone match(TrieNode node, String text, TParsePosition position) { - prepareTrie(); int start = position.getIndex(); int index = start; int lastMatch = start; TTimeZone tz = null; - while (node.childNodes.length > 0) { + while (node.childNodes != null && node.childNodes.length > 0) { if (node.tz != null) { lastMatch = index; tz = node.tz; @@ -432,7 +432,11 @@ abstract class TDateFormatElement { if (next < 0) { return null; } - node = node.childNodes[index]; + node = node.childNodes[next]; + } + if (node.tz != null) { + lastMatch = index; + tz = node.tz; } position.setIndex(lastMatch); return tz; @@ -459,6 +463,7 @@ abstract class TDateFormatElement { TTimeZone tz = TTimeZone.getTimeZone(tzId); builder.add(tz.getID(), tz); } + idSearchTrie = builder.build(); } } @@ -485,6 +490,7 @@ abstract class TDateFormatElement { position.setErrorIndex(index); return; } + ++index; if (index + 2 > text.length() || !Character.isDigit(text.charAt(index)) || !Character.isDigit(text.charAt(index + 1))) { @@ -511,10 +517,9 @@ abstract class TDateFormatElement { TrieNodeBuilder root = new TrieNodeBuilder(); public void add(String text, TTimeZone tz) { - int index = 0; TrieNodeBuilder node = root; - while (index < text.length()) { - char c = Character.toLowerCase(text.charAt(index)); + for (int i = 0; i < text.length(); ++i) { + char c = Character.toLowerCase(text.charAt(i)); while (node.ch != c) { if (node.ch == '\0') { node.ch = c; @@ -537,12 +542,15 @@ abstract class TDateFormatElement { TrieNode build(TrieNodeBuilder builder) { TrieNode node = new TrieNode(); + if (builder == null) { + return node; + } node.tz = builder.tz; List builders = new ArrayList<>(); TrieNodeBuilder tmp = builder; while (tmp.ch != '\0') { - builders.add(builder); - builder = builder.sibling; + builders.add(tmp); + tmp = tmp.sibling; } Collections.sort(builders, new Comparator() { @Override public int compare(TrieNodeBuilder o1, TrieNodeBuilder o2) { @@ -553,7 +561,7 @@ abstract class TDateFormatElement { node.childNodes = new TrieNode[builders.size()]; for (int i = 0; i < node.chars.length; ++i) { node.chars[i] = builders.get(i).ch; - node.childNodes[i] = build(builders.get(i)); + node.childNodes[i] = build(builders.get(i).next); } return node; } diff --git a/teavm-core/src/main/java/org/teavm/model/MethodReference.java b/teavm-core/src/main/java/org/teavm/model/MethodReference.java index 9c34fff4d..4e196af0e 100644 --- a/teavm-core/src/main/java/org/teavm/model/MethodReference.java +++ b/teavm-core/src/main/java/org/teavm/model/MethodReference.java @@ -139,6 +139,12 @@ public class MethodReference { 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() { StringBuilder sb = new StringBuilder(); sb.append('('); diff --git a/teavm-core/src/main/java/org/teavm/model/emit/ForkEmitter.java b/teavm-core/src/main/java/org/teavm/model/emit/ForkEmitter.java new file mode 100644 index 000000000..5c410e37b --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/emit/ForkEmitter.java @@ -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); + } + }; + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java b/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java new file mode 100644 index 000000000..40cc20483 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java @@ -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); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/emit/ValueEmitter.java b/teavm-core/src/main/java/org/teavm/model/emit/ValueEmitter.java new file mode 100644 index 000000000..bb5687d38 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/emit/ValueEmitter.java @@ -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); + } +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java index 3d44de603..ded5eff87 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java @@ -33,18 +33,12 @@ import org.teavm.platform.metadata.Resource; public class MetadataProviderNativeGenerator implements Generator { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { - // Validate method ClassReader cls = context.getClassSource().get(methodRef.getClassName()); MethodReader method = cls.getMethod(methodRef.getDescriptor()); AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName()); - if (providerAnnot == null) { - return; - } - 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; - } + + AnnotationReader refAnnot = method.getAnnotations().get(MetadataProviderRef.class.getName()); + methodRef = MethodReference.parse(refAnnot.getValue("value").getString()); // Find and instantiate metadata generator ValueType generatorType = providerAnnot.getValue("value").getJavaClass(); @@ -78,12 +72,8 @@ public class MetadataProviderNativeGenerator implements Generator { // Generate resource loader Resource resource = generator.generateMetadata(metadataContext, methodRef); - writer.append("if (!window.hasOwnProperty(\"").appendMethodBody(methodRef).append("$$resource\")) {") - .indent().softNewLine(); - writer.append("window.").appendMethodBody(methodRef).append("$$resource = "); + writer.append("return "); ResourceWriterHelper.write(writer, resource); writer.append(';').softNewLine(); - writer.outdent().append('}').softNewLine(); - writer.append("return ").appendMethodBody(methodRef).append("$$resource;").softNewLine(); } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderRef.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderRef.java new file mode 100644 index 000000000..2f8f560b6 --- /dev/null +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderRef.java @@ -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(); +} diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java index 0bf498544..2ac8a0bc9 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/MetadataProviderTransformer.java @@ -18,6 +18,10 @@ package org.teavm.platform.plugin; import org.teavm.diagnostics.Diagnostics; import org.teavm.javascript.spi.GeneratedBy; 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.metadata.ClassScopedMetadataProvider; import org.teavm.platform.metadata.MetadataProvider; @@ -27,15 +31,14 @@ import org.teavm.platform.metadata.MetadataProvider; * @author Alexey Andreev */ class MetadataProviderTransformer implements ClassHolderTransformer { + static int fieldIdGen; + @Override 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()); if (providerAnnot != null) { - AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName()); - genAnnot.getValues().put("value", new AnnotationValue(ValueType.object( - MetadataProviderNativeGenerator.class.getName()))); - method.getAnnotations().add(genAnnot); + transformMetadataMethod(cls, method, diagnostics); } providerAnnot = method.getAnnotations().get(ClassScopedMetadataProvider.class.getName()); 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; + } }