diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/lambda/LambdaMetafactorySubstitutor.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/lambda/LambdaMetafactorySubstitutor.java index aeaace910..d82ca49cf 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/lambda/LambdaMetafactorySubstitutor.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/lambda/LambdaMetafactorySubstitutor.java @@ -44,12 +44,14 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor } int capturedVarCount = callSite.getCalledMethod().parameterCount(); - MethodHolder ctor = createConstructor(implementor, Arrays.copyOfRange(invokedType, 0, capturedVarCount)); - createBridge(implementor, callSite.getCalledMethod().getName(), instantiatedMethodType, samMethodType); + MethodHolder ctor = createConstructor(classSource, implementor, + Arrays.copyOfRange(invokedType, 0, capturedVarCount)); + createBridge(classSource, implementor, callSite.getCalledMethod().getName(), instantiatedMethodType, + samMethodType); MethodHolder worker = new MethodHolder(callSite.getCalledMethod().getName(), instantiatedMethodType); worker.setLevel(AccessLevel.PUBLIC); - ProgramEmitter pe = ProgramEmitter.create(worker); + ProgramEmitter pe = ProgramEmitter.create(worker, callSite.getAgent().getClassSource()); ValueEmitter thisVar = pe.var(0, implementor); ValueEmitter[] arguments = new ValueEmitter[instantiatedMethodType.length - 1]; for (int i = 0; i < arguments.length; ++i) { @@ -199,13 +201,13 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor } } - private MethodHolder createConstructor(ClassHolder implementor, ValueType[] types) { + private MethodHolder createConstructor(ClassReaderSource classSource, ClassHolder implementor, ValueType[] types) { ValueType[] signature = Arrays.copyOf(types, types.length + 1); signature[types.length] = ValueType.VOID; MethodHolder ctor = new MethodHolder("", signature); ctor.setLevel(AccessLevel.PUBLIC); - ProgramEmitter pe = ProgramEmitter.create(ctor); + ProgramEmitter pe = ProgramEmitter.create(ctor, classSource); ValueEmitter thisVar = pe.var(0, implementor); thisVar.invokeSpecial(implementor.getParent(), ""); @@ -222,7 +224,8 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor return ctor; } - private void createBridge(ClassHolder implementor, String name, ValueType[] types, ValueType[] bridgeTypes) { + private void createBridge(ClassReaderSource classSource, ClassHolder implementor, String name, ValueType[] types, + ValueType[] bridgeTypes) { if (Arrays.equals(types, bridgeTypes)) { return; } @@ -230,7 +233,7 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor MethodHolder bridge = new MethodHolder(name, bridgeTypes); bridge.setLevel(AccessLevel.PUBLIC); bridge.getModifiers().add(ElementModifier.BRIDGE); - ProgramEmitter pe = ProgramEmitter.create(bridge); + ProgramEmitter pe = ProgramEmitter.create(bridge, classSource); ValueEmitter thisVar = pe.var(0, implementor); ValueEmitter[] arguments = new ValueEmitter[bridgeTypes.length - 1]; for (int i = 0; i < arguments.length; ++i) { @@ -239,7 +242,7 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor for (int i = 0; i < bridgeTypes.length - 1; ++i) { ValueType type = types[i]; - ValueType bridgeType = types[i]; + ValueType bridgeType = bridgeTypes[i]; if (type.equals(bridgeType)) { continue; } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java index 50cab1280..b214d0eaa 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/AnnotationDependencyListener.java @@ -90,7 +90,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { implementor.addField(field); MethodHolder accessor = new MethodHolder(methodDecl.getDescriptor()); - ProgramEmitter pe = ProgramEmitter.create(accessor); + ProgramEmitter pe = ProgramEmitter.create(accessor, classSource); ValueEmitter thisVal = pe.var(0, implementor); ValueEmitter result = thisVal.getField(field.getName(), field.getType()); if (field.getType() instanceof ValueType.Array) { @@ -104,7 +104,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { ctorSignature.add(ValueType.VOID); MethodHolder ctor = new MethodHolder("", ctorSignature.toArray(new ValueType[ctorSignature.size()])); - ProgramEmitter pe = ProgramEmitter.create(ctor); + ProgramEmitter pe = ProgramEmitter.create(ctor, classSource); ValueEmitter thisVar = pe.var(0, implementor); thisVar.invokeSpecial(Object.class, ""); int index = 1; @@ -119,7 +119,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { implementor.addMethod(ctor); MethodHolder annotTypeMethod = new MethodHolder("annotationType", ValueType.parse(Class.class)); - pe = ProgramEmitter.create(annotTypeMethod); + pe = ProgramEmitter.create(annotTypeMethod, classSource); pe.constant(ValueType.object(annotationType)).returnValue(); implementor.addMethod(annotTypeMethod); @@ -164,7 +164,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { MethodHolder ctor = new MethodHolder("", ValueType.VOID); ctor.setLevel(AccessLevel.PUBLIC); - ProgramEmitter pe = ProgramEmitter.create(ctor); + ProgramEmitter pe = ProgramEmitter.create(ctor, agent.getClassSource()); ValueEmitter thisVar = pe.var(0, cls); thisVar.invokeSpecial(Object.class, "").exit(); @@ -178,7 +178,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { private MethodHolder addReader(DependencyAgent agent, ClassReader cls) { MethodHolder readerMethod = new MethodHolder("getAnnotations", ValueType.parse(Annotation[].class)); readerMethod.setLevel(AccessLevel.PUBLIC); - ProgramEmitter pe = ProgramEmitter.create(readerMethod); + ProgramEmitter pe = ProgramEmitter.create(readerMethod, agent.getClassSource()); List annotations = new ArrayList<>(); for (AnnotationReader annot : cls.getAnnotations().all()) { @@ -198,7 +198,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { ValueEmitter array = pe.constructArray(Annotation.class, annotations.size()); for (int i = 0; i < annotations.size(); ++i) { - array.unwrapArray().setElement(i, generateAnnotationInstance(agent, pe, annotations.get(i))); + array.setElement(i, generateAnnotationInstance(agent, pe, annotations.get(i))); } array.returnValue(); @@ -250,7 +250,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener { ValueType itemType = ((ValueType.Array) type).getItemType(); ValueEmitter array = pe.constructArray(itemType, list.size()); for (int i = 0; i < list.size(); ++i) { - array.unwrapArray().setElement(i, generateAnnotationValue(agent, pe, itemType, list.get(i))); + array.setElement(i, generateAnnotationValue(agent, pe, itemType, list.get(i))); } return array; } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index d9988af3f..2d27966a1 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -187,7 +187,7 @@ class DependencyGraphBuilder { if (program == null) { return; } - ProgramEmitter pe = ProgramEmitter.create(program); + ProgramEmitter pe = ProgramEmitter.create(program, dependencyChecker.getClassSource()); for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); for (int j = 0; j < block.getInstructions().size(); ++j) { @@ -229,7 +229,7 @@ class DependencyGraphBuilder { } } - pe.setBlock(block); + pe.enter(block); pe.setCurrentLocation(indy.getLocation()); block.getInstructions().remove(j); diff --git a/teavm-core/src/main/java/org/teavm/model/ClassReaderSource.java b/teavm-core/src/main/java/org/teavm/model/ClassReaderSource.java index 93d9089da..dd3b8f482 100644 --- a/teavm-core/src/main/java/org/teavm/model/ClassReaderSource.java +++ b/teavm-core/src/main/java/org/teavm/model/ClassReaderSource.java @@ -19,6 +19,7 @@ import java.util.ArrayDeque; import java.util.Deque; import java.util.HashSet; import java.util.Iterator; +import java.util.Optional; import java.util.Set; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -113,24 +114,44 @@ public interface ClassReaderSource { .filter(candidate -> candidate != null); } - default boolean isSuperType(String superType, String subType) { + default Optional isSuperType(String superType, String subType) { if (superType.equals(subType)) { - return true; + return Optional.of(true); } ClassReader cls = get(subType); if (subType == null) { - return false; + return Optional.empty(); } if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) { - if (isSuperType(superType, cls.getParent())) { - return true; + if (isSuperType(superType, cls.getParent()).orElse(false)) { + return Optional.of(true); } } for (String iface : cls.getInterfaces()) { - if (isSuperType(superType, iface)) { - return true; + if (isSuperType(superType, iface).orElse(false)) { + return Optional.of(true); } } - return false; + return Optional.of(false); + } + + default Optional isSuperType(ValueType superType, ValueType subType) { + if (superType.equals(subType)) { + return Optional.of(true); + } + if (superType instanceof ValueType.Primitive || subType instanceof ValueType.Primitive) { + return Optional.of(false); + } + if (superType.isObject("java.lang.Object")) { + return Optional.of(true); + } + if (superType instanceof ValueType.Object && subType instanceof ValueType.Object) { + return isSuperType(((ValueType.Object) superType).getClassName(), + ((ValueType.Object) subType).getClassName()); + } else if (superType instanceof ValueType.Array & subType instanceof ValueType.Array) { + return isSuperType(((ValueType.Array) superType).getItemType(), ((ValueType.Array) subType).getItemType()); + } else { + return Optional.of(false); + } } } diff --git a/teavm-core/src/main/java/org/teavm/model/emit/ChooseEmitter.java b/teavm-core/src/main/java/org/teavm/model/emit/ChooseEmitter.java index c7886828e..18d14d1b0 100644 --- a/teavm-core/src/main/java/org/teavm/model/emit/ChooseEmitter.java +++ b/teavm-core/src/main/java/org/teavm/model/emit/ChooseEmitter.java @@ -38,14 +38,16 @@ public class ChooseEmitter { public ChooseEmitter option(int value, FragmentEmitter fragment) { SwitchTableEntry entry = new SwitchTableEntry(); entry.setCondition(value); - entry.setTarget(pe.createBlock()); + entry.setTarget(pe.prepareBlock()); + pe.enter(entry.getTarget()); fragment.emit(); pe.jump(joinBlock); return this; } public ProgramEmitter otherwise(FragmentEmitter fragment) { - insn.setDefaultTarget(pe.createBlock()); + insn.setDefaultTarget(pe.prepareBlock()); + pe.enter(insn.getDefaultTarget()); fragment.emit(); pe.jump(joinBlock); return pe; diff --git a/teavm-core/src/main/java/org/teavm/model/emit/ConditionEmitter.java b/teavm-core/src/main/java/org/teavm/model/emit/ConditionEmitter.java index 457fc6b26..ea8101e83 100644 --- a/teavm-core/src/main/java/org/teavm/model/emit/ConditionEmitter.java +++ b/teavm-core/src/main/java/org/teavm/model/emit/ConditionEmitter.java @@ -16,8 +16,6 @@ package org.teavm.model.emit; import org.teavm.model.BasicBlock; -import org.teavm.model.instructions.BinaryBranchingCondition; -import org.teavm.model.instructions.BranchingCondition; /** * @@ -25,38 +23,26 @@ import org.teavm.model.instructions.BranchingCondition; */ public class ConditionEmitter { private ProgramEmitter pe; - private ComputationEmitter argument; - private BasicBlock join; + ForkEmitter fork; - ConditionEmitter(ProgramEmitter pe, ComputationEmitter argument, BasicBlock join) { + ConditionEmitter(ProgramEmitter pe, ForkEmitter fork) { this.pe = pe; - this.argument = argument; - this.join = join; + this.fork = fork; } - public IfEmitter isTrue() { - return new IfEmitter(pe, argument.emit().fork(BranchingCondition.NOT_EQUAL), join); + public ConditionEmitter and(ConditionProducer other) { + BasicBlock block = pe.prepareBlock(); + pe.enter(block); + ConditionEmitter otherEmitter = other.produce(); + ForkEmitter newFork = fork.and(block, otherEmitter.fork); + return new ConditionEmitter(pe, newFork); } - public IfEmitter isFalse() { - return new IfEmitter(pe, argument.emit().fork(BranchingCondition.NOT_NULL), join); - } - - public IfEmitter equalTo(ComputationEmitter other) { - return new IfEmitter(pe, argument.emit().fork(BinaryBranchingCondition.EQUAL, other.emit()), join); - } - - public IfEmitter notEqualTo(ComputationEmitter other) { - return new IfEmitter(pe, argument.emit().fork(BinaryBranchingCondition.NOT_EQUAL, other.emit()), join); - } - - public IfEmitter sameAs(ComputationEmitter other) { - return new IfEmitter(pe, argument.emit().fork(BinaryBranchingCondition.REFERENCE_EQUAL, other.emit()), - join); - } - - public IfEmitter notSameAs(ComputationEmitter other) { - return new IfEmitter(pe, argument.emit().fork(BinaryBranchingCondition.REFERENCE_NOT_EQUAL, other.emit()), - join); + public ConditionEmitter or(ConditionProducer other) { + BasicBlock block = pe.prepareBlock(); + pe.enter(block); + ConditionEmitter otherEmitter = other.produce(); + ForkEmitter newFork = fork.or(block, otherEmitter.fork); + return new ConditionEmitter(pe, newFork); } } diff --git a/teavm-core/src/main/java/org/teavm/model/emit/ConditionProducer.java b/teavm-core/src/main/java/org/teavm/model/emit/ConditionProducer.java new file mode 100644 index 000000000..3beb8b697 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/emit/ConditionProducer.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * + * @author Alexey Andreev + */ +@FunctionalInterface +public interface ConditionProducer { + ConditionEmitter produce(); +} diff --git a/teavm-core/src/main/java/org/teavm/model/emit/EmitException.java b/teavm-core/src/main/java/org/teavm/model/emit/EmitException.java new file mode 100644 index 000000000..05fe7c0a7 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/emit/EmitException.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * + * @author Alexey Andreev + */ +public class EmitException extends RuntimeException { + private static final long serialVersionUID = 7479098357353665286L; + + public EmitException() { + super(); + } + + public EmitException(String message) { + super(message); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/emit/IfEmitter.java b/teavm-core/src/main/java/org/teavm/model/emit/IfEmitter.java index 45bf9223f..ef49956ce 100644 --- a/teavm-core/src/main/java/org/teavm/model/emit/IfEmitter.java +++ b/teavm-core/src/main/java/org/teavm/model/emit/IfEmitter.java @@ -30,25 +30,23 @@ public class IfEmitter { this.pe = pe; this.fork = fork; this.join = join; - } - - public ConditionEmitter and(ComputationEmitter condition) { - return new ConditionEmitter(pe, condition, join); - } - - public ConditionEmitter or(ComputationEmitter condition) { - return new ConditionEmitter(pe, condition, join); + fork.setThen(join); + fork.setElse(join); } public IfEmitter thenDo(FragmentEmitter fragment) { - fork.setThen(pe.createBlock()); + BasicBlock block = pe.prepareBlock(); + fork.setThen(block); + pe.enter(block); fragment.emit(); pe.jump(join); return this; } public IfEmitter elseDo(FragmentEmitter fragment) { - fork.setThen(pe.createBlock()); + BasicBlock block = pe.prepareBlock(); + fork.setThen(block); + pe.enter(block); fragment.emit(); pe.jump(join); return this; 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 index f0fba9339..b4bf48b74 100644 --- a/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java +++ b/teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java @@ -17,6 +17,8 @@ package org.teavm.model.emit; import org.teavm.model.BasicBlock; import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.FieldReader; import org.teavm.model.FieldReference; import org.teavm.model.Instruction; import org.teavm.model.InstructionLocation; @@ -51,11 +53,13 @@ import org.teavm.model.instructions.SwitchInstruction; public final class ProgramEmitter { private Program program; private BasicBlock block; + ClassReaderSource classSource; private InstructionLocation currentLocation; - private ProgramEmitter(Program program, BasicBlock block) { + private ProgramEmitter(Program program, BasicBlock block, ClassReaderSource classSource) { this.program = program; this.block = block; + this.classSource = classSource; } public Program getProgram() { @@ -66,14 +70,13 @@ public final class ProgramEmitter { return block; } - public void setBlock(BasicBlock block) { + public ProgramEmitter enter(BasicBlock block) { this.block = block; + return this; } - public BasicBlock createBlock() { - BasicBlock block = program.createBasicBlock(); - setBlock(block); - return block; + public BasicBlock prepareBlock() { + return program.createBasicBlock(); } public ValueEmitter constant(Class cls) { @@ -143,6 +146,11 @@ public final class ProgramEmitter { } public ValueEmitter getField(FieldReference field, ValueType type) { + FieldReader resolvedField = classSource.resolve(field); + if (resolvedField != null) { + field = resolvedField.getReference(); + } + Variable var = program.createVariable(); GetFieldInstruction insn = new GetFieldInstruction(); insn.setField(field); @@ -161,6 +169,11 @@ public final class ProgramEmitter { } public ProgramEmitter setField(FieldReference field, ValueEmitter value) { + FieldReader resolvedField = classSource.resolve(field); + if (resolvedField != null) { + field = resolvedField.getReference(); + } + PutFieldInstruction insn = new PutFieldInstruction(); insn.setField(field); insn.setFieldType(value.type); @@ -173,6 +186,30 @@ public final class ProgramEmitter { return setField(new FieldReference(className, fieldName), value); } + public ValueEmitter invoke(MethodReference method, ValueEmitter... arguments) { + for (int i = 0; i < method.parameterCount(); ++i) { + if (!classSource.isSuperType(method.parameterType(i), arguments[i].getType()).orElse(true)) { + throw new EmitException("Argument " + i + " of type " + arguments[i].getType() + " is " + + "not compatible with method " + method); + } + } + + 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 ? var(result, method.getReturnType()) : null; + } + public ValueEmitter invoke(String className, String methodName, ValueType resultType, ValueEmitter... arguments) { Variable result = null; if (resultType != ValueType.VOID) { @@ -314,7 +351,7 @@ public final class ProgramEmitter { block.getInstructions().add(insn); } - public static ProgramEmitter create(MethodHolder method) { + public static ProgramEmitter create(MethodHolder method, ClassReaderSource classSource) { Program program = new Program(); method.setProgram(program); BasicBlock zeroBlock = program.createBasicBlock(); @@ -329,11 +366,15 @@ public final class ProgramEmitter { program.createVariable(); } - return new ProgramEmitter(program, block); + return new ProgramEmitter(program, block, classSource); } - public ConditionEmitter when(ComputationEmitter condition) { - return new ConditionEmitter(this, condition, program.createBasicBlock()); + public IfEmitter when(ConditionEmitter cond) { + return new IfEmitter(this, cond.fork, prepareBlock()); + } + + public IfEmitter when(ConditionProducer cond) { + return when(cond.produce()); } public PhiEmitter phi(ValueType type, BasicBlock block) { @@ -370,7 +411,11 @@ public final class ProgramEmitter { return new ChooseEmitter(this, insn, program.createBasicBlock()); } - public static ProgramEmitter create(Program program) { - return new ProgramEmitter(program, null); + public ClassReaderSource getClassSource() { + return classSource; + } + + public static ProgramEmitter create(Program program, ClassReaderSource classSource) { + return new ProgramEmitter(program, null, classSource); } } 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 index 21aa58dcc..21b6c17c1 100644 --- a/teavm-core/src/main/java/org/teavm/model/emit/ValueEmitter.java +++ b/teavm-core/src/main/java/org/teavm/model/emit/ValueEmitter.java @@ -16,8 +16,10 @@ package org.teavm.model.emit; import org.teavm.model.BasicBlock; +import org.teavm.model.ClassReaderSource; import org.teavm.model.FieldReference; import org.teavm.model.Incoming; +import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.Phi; import org.teavm.model.PrimitiveType; @@ -84,7 +86,7 @@ public class ValueEmitter { public ValueEmitter getField(String name, ValueType type) { if (!(this.type instanceof ValueType.Object)) { - throw new IllegalStateException("Can't get field of non-object type: " + type); + throw new EmitException("Can't get field of non-object type: " + type); } String className = ((ValueType.Object) this.type).getClassName(); @@ -102,9 +104,9 @@ public class ValueEmitter { return getField(name, ValueType.parse(type)); } - public void setField(String name, ValueEmitter value) { + public ProgramEmitter setField(String name, ValueEmitter value) { if (!(type instanceof ValueType.Object)) { - throw new IllegalStateException("Can't get field of non-object type: " + type); + throw new EmitException("Can't get field of non-object type: " + type); } String className = ((ValueType.Object) type).getClassName(); @@ -114,6 +116,7 @@ public class ValueEmitter { insn.setInstance(variable); insn.setValue(value.getVariable()); pe.addInstruction(insn); + return pe; } static class Pair { @@ -128,20 +131,20 @@ public class ValueEmitter { private Pair commonNumeric(ValueEmitter other) { if (!(type instanceof ValueType.Primitive)) { - throw new IllegalArgumentException("First argument is not a primitive: " + type); + throw new EmitException("First argument is not a primitive: " + type); } if (!(other.type instanceof ValueType.Primitive)) { - throw new IllegalArgumentException("First argument is not a primitive: " + other.type); + throw new EmitException("First argument is not a primitive: " + other.type); } PrimitiveType firstType = ((ValueType.Primitive) type).getKind(); PrimitiveType secondType = ((ValueType.Primitive) other.type).getKind(); if (firstType == PrimitiveType.BOOLEAN) { - throw new IllegalArgumentException("First argument is not numeric: " + type); + throw new EmitException("First argument is not numeric: " + type); } if (secondType == PrimitiveType.BOOLEAN) { - throw new IllegalArgumentException("Second argument is not numeric: " + other.type); + throw new EmitException("Second argument is not numeric: " + other.type); } ValueEmitter a = this; @@ -312,7 +315,7 @@ public class ValueEmitter { switch (common) { case FLOAT: case DOUBLE: - throw new IllegalArgumentException("Can't perform bitwise operation between non-integers: " + common); + throw new EmitException("Can't perform bitwise operation between non-integers: " + common); default: break; } @@ -364,7 +367,7 @@ public class ValueEmitter { private ValueEmitter shift(BinaryOperation op, ValueEmitter other) { if (!(type instanceof ValueType.Primitive) || !(other.type instanceof ValueType.Primitive)) { - throw new IllegalArgumentException("Can't shift " + type + " by " + other.type); + throw new EmitException("Can't shift " + type + " by " + other.type); } ValueType valueType = type; @@ -372,7 +375,7 @@ public class ValueEmitter { switch (kind) { case FLOAT: case DOUBLE: - throw new IllegalArgumentException("Can't perform bit shift operation over non-integer: " + type); + throw new EmitException("Can't perform bit shift operation over non-integer: " + type); default: break; } @@ -384,7 +387,7 @@ public class ValueEmitter { case INTEGER: break; default: - throw new IllegalArgumentException("Can't perform bit shift operation with non-integer " + throw new EmitException("Can't perform bit shift operation with non-integer " + "shift: " + type); } other = other.castToInteger(convertToIntegerSubtype(shiftKind)); @@ -399,10 +402,48 @@ public class ValueEmitter { return binaryOp(op, value, other, valueType); } + public ValueEmitter invoke(InvocationType invokeType, MethodReference method, ValueEmitter... arguments) { + if (!(type instanceof ValueType.Object)) { + throw new EmitException("Can't invoke method on non-object type: " + type); + } + + ClassReaderSource classSource = pe.getClassSource(); + for (int i = 0; i < method.parameterCount(); ++i) { + if (!classSource.isSuperType(method.parameterType(i), arguments[i].getType()).orElse(false)) { + throw new EmitException("Argument " + i + " of type " + arguments[i].getType() + " is " + + "not compatible with method " + method); + } + } + + if (!pe.classSource.isSuperType(((ValueType.Object) type).getClassName(), method.getClassName()) + .orElse(true)) { + throw new EmitException("Can't call " + method + " on non-compatible class " + type); + } + MethodReader resolvedMethod = pe.classSource.resolve(method); + if (resolvedMethod != null) { + method = resolvedMethod.getReference(); + } + + Variable result = null; + if (method.getReturnType() != ValueType.VOID) { + result = pe.getProgram().createVariable(); + } + InvokeInstruction insn = new InvokeInstruction(); + insn.setType(invokeType); + 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.var(result, method.getReturnType()) : null; + } + public ValueEmitter invoke(InvocationType invokeType, String className, String name, ValueType resultType, ValueEmitter... arguments) { if (!(type instanceof ValueType.Object)) { - throw new IllegalStateException("Can't invoke method on non-object type: " + type); + throw new EmitException("Can't invoke method on non-object type: " + type); } Variable result = null; @@ -433,6 +474,10 @@ public class ValueEmitter { return invoke(invokeType, ((ValueType.Object) type).getClassName(), name, resultType, arguments); } + public ValueEmitter invokeSpecial(MethodReference method, ValueEmitter... arguments) { + return invoke(InvocationType.SPECIAL, method, arguments); + } + public ValueEmitter invokeSpecial(String className, String name, ValueType resultType, ValueEmitter... arguments) { return invoke(InvocationType.SPECIAL, className, name, resultType, arguments); } @@ -464,15 +509,19 @@ public class ValueEmitter { return invoke(InvocationType.VIRTUAL, name, resultType, arguments); } + public ValueEmitter invokeVirtual(MethodReference method, ValueEmitter... arguments) { + return invoke(InvocationType.VIRTUAL, method, arguments); + } + public ValueEmitter invokeVirtual(String name, Class resultType, ValueEmitter... arguments) { return invoke(InvocationType.VIRTUAL, name, ValueType.parse(resultType), arguments); } - public void invokeVirtual(String name, ValueEmitter... arguments) { + public ProgramEmitter invokeVirtual(String name, ValueEmitter... arguments) { invokeVirtual(name, ValueType.VOID, arguments); + return pe; } - public ValueEmitter join(BasicBlock block, ValueEmitter other, BasicBlock otherBlock, ValueType type) { Variable var = pe.getProgram().createVariable(); Phi phi = new Phi(); @@ -522,6 +571,38 @@ public class ValueEmitter { }; } + public ConditionEmitter isTrue() { + return new ConditionEmitter(pe, fork(BranchingCondition.NOT_EQUAL)); + } + + public ConditionEmitter isFalse() { + return new ConditionEmitter(pe, fork(BranchingCondition.EQUAL)); + } + + public ConditionEmitter isEqualTo(ValueEmitter other) { + return new ConditionEmitter(pe, fork(BinaryBranchingCondition.NOT_EQUAL, other)); + } + + public ConditionEmitter isNotEqualTo(ValueEmitter other) { + return new ConditionEmitter(pe, fork(BinaryBranchingCondition.EQUAL, other)); + } + + public ConditionEmitter isGreaterThan(ValueEmitter other) { + return new ConditionEmitter(pe, compareTo(other).fork(BranchingCondition.GREATER)); + } + + public ConditionEmitter isGreaterOrEqualTo(ValueEmitter other) { + return new ConditionEmitter(pe, compareTo(other).fork(BranchingCondition.GREATER_OR_EQUAL)); + } + + public ConditionEmitter isLessThan(ValueEmitter other) { + return new ConditionEmitter(pe, compareTo(other).fork(BranchingCondition.LESS)); + } + + public ConditionEmitter isLessOrEuqalTo(ValueEmitter other) { + return new ConditionEmitter(pe, compareTo(other).fork(BranchingCondition.LESS_OR_EQUAL)); + } + public void returnValue() { ExitInstruction insn = new ExitInstruction(); insn.setValueToReturn(variable); @@ -529,6 +610,10 @@ public class ValueEmitter { } public void raise() { + if (!pe.classSource.isSuperType(ValueType.object("java.lang.Throwable"), type).orElse(true)) { + throw new EmitException("Can't throw non-exception value: " + type); + } + RaiseInstruction insn = new RaiseInstruction(); insn.setException(variable); pe.addInstruction(insn); @@ -539,12 +624,13 @@ public class ValueEmitter { } public ValueEmitter cast(ValueType type) { - if (type.equals(this.type)) { + if (type.equals(this.type) || pe.classSource.isSuperType(type, this.type).orElse(false)) { return this; } + if (type instanceof ValueType.Primitive) { if (!(this.type instanceof ValueType.Primitive)) { - throw new IllegalStateException("Can't convert " + this.type + " to " + type); + throw new EmitException("Can't convert " + this.type + " to " + type); } ValueEmitter value = this; @@ -552,7 +638,7 @@ public class ValueEmitter { PrimitiveType targetKind = ((ValueType.Primitive) type).getKind(); if (sourceKind == PrimitiveType.BOOLEAN || targetKind == PrimitiveType.BOOLEAN) { - throw new IllegalStateException("Can't convert " + this.type + " to " + type); + throw new EmitException("Can't convert " + this.type + " to " + type); } IntegerSubtype sourceSubtype = convertToIntegerSubtype(sourceKind); @@ -577,7 +663,7 @@ public class ValueEmitter { return value; } else { if (this.type instanceof ValueType.Primitive) { - throw new IllegalStateException("Can't convert " + this.type + " to " + type); + throw new EmitException("Can't convert " + this.type + " to " + type); } Variable result = pe.getProgram().createVariable(); CastInstruction insn = new CastInstruction(); @@ -591,7 +677,7 @@ public class ValueEmitter { public ValueEmitter cast(NumericOperandType to) { if (!(type instanceof ValueType.Primitive)) { - throw new IllegalStateException("Can't cast non-primitive type: " + type); + throw new EmitException("Can't cast non-primitive type: " + type); } ValueEmitter value = this; @@ -611,7 +697,11 @@ public class ValueEmitter { return result; } - private ValueEmitter castFromInteger(IntegerSubtype subtype) { + public ValueEmitter castFromInteger(IntegerSubtype subtype) { + if (type != ValueType.INTEGER) { + throw new EmitException("Can't cast non-integer value: " + type); + } + CastIntegerInstruction insn = new CastIntegerInstruction(subtype, CastIntegerDirection.TO_INTEGER); insn.setValue(variable); ValueEmitter result = pe.newVar(ValueType.INTEGER); @@ -620,7 +710,25 @@ public class ValueEmitter { return result; } - private ValueEmitter castToInteger(IntegerSubtype subtype) { + public ValueEmitter castToInteger(IntegerSubtype subtype) { + switch (subtype) { + case BYTE: + if (type != ValueType.BYTE) { + throw new EmitException("Can't cast non-byte value: " + type); + } + break; + case SHORT: + if (type != ValueType.SHORT) { + throw new EmitException("Can't cast non-short value: " + type); + } + break; + case CHARACTER: + if (type != ValueType.CHARACTER) { + throw new EmitException("Can't cast non-char value: " + type); + } + break; + } + CastIntegerInstruction insn = new CastIntegerInstruction(subtype, CastIntegerDirection.FROM_INTEGER); insn.setValue(variable); ValueEmitter result = pe.newVar(ValueType.INTEGER); @@ -629,15 +737,44 @@ public class ValueEmitter { return result; } - public ValueEmitter getElement(ValueEmitter index) { - if (!(type instanceof ValueType.Array)) { - throw new IllegalArgumentException("Can't get element of non-array type: " + type); + public ValueEmitter widenToInteger() { + if (!(type instanceof ValueType.Primitive)) { + throw new EmitException("Can't widen non-primitive: " + type); } + PrimitiveType primitive = ((ValueType.Primitive) type).getKind(); + if (primitive == PrimitiveType.INTEGER) { + return this; + } + IntegerSubtype subtype = convertToIntegerSubtype(primitive); + if (subtype == null) { + throw new EmitException("Can't widen to int: " + type); + } + + return castToInteger(subtype); + } + + public ValueEmitter assertIs(ValueType type) { + if (!pe.classSource.isSuperType(type, this.type).orElse(true)) { + throw new EmitException("Value type " + this.type + " is not subtype of " + type); + } + return this; + } + + public ValueEmitter assertIs(Class type) { + return assertIs(ValueType.parse(type)); + } + + public ValueEmitter getElement(ValueEmitter index) { + if (!(type instanceof ValueType.Array)) { + throw new EmitException("Can't get element of non-array type: " + type); + } + + ValueEmitter array = unwrapArray(); Variable result = pe.getProgram().createVariable(); GetElementInstruction insn = new GetElementInstruction(); - insn.setArray(variable); - insn.setIndex(index.variable); + insn.setArray(array.variable); + insn.setIndex(index.widenToInteger().variable); insn.setReceiver(result); pe.addInstruction(insn); return pe.var(result, ((ValueType.Array) type).getItemType()); @@ -647,23 +784,25 @@ public class ValueEmitter { return getElement(pe.constant(index)); } - public void setElement(ValueEmitter index, ValueEmitter value) { - PutElementInstruction insn = new PutElementInstruction(); - insn.setArray(variable); - insn.setIndex(index.variable); - insn.setValue(value.variable); - pe.addInstruction(insn); - } - - public void setElement(int index, ValueEmitter value) { - setElement(pe.constant(index), value); - } - - public ValueEmitter unwrapArray() { + public ProgramEmitter setElement(ValueEmitter index, ValueEmitter value) { if (!(type instanceof ValueType.Array)) { - throw new IllegalStateException("Can't unwrap non-array value: " + type); + throw new EmitException("Can't set element of non-array type: " + type); } + PutElementInstruction insn = new PutElementInstruction(); + insn.setArray(unwrapArray().variable); + insn.setIndex(index.widenToInteger().variable); + insn.setValue(value.variable); + pe.addInstruction(insn); + return pe; + } + + public ProgramEmitter setElement(int index, ValueEmitter value) { + setElement(pe.constant(index), value); + return pe; + } + + private ValueEmitter unwrapArray() { ValueType elementType = ((ValueType.Array) type).getItemType(); Variable result = pe.getProgram().createVariable(); UnwrapArrayInstruction insn = new UnwrapArrayInstruction(getArrayElementType(elementType)); @@ -674,9 +813,13 @@ public class ValueEmitter { } public ValueEmitter arrayLength() { + if (!(type instanceof ValueType.Array)) { + throw new EmitException("Can't get length of non-array type: " + type); + } + Variable result = pe.getProgram().createVariable(); ArrayLengthInstruction insn = new ArrayLengthInstruction(); - insn.setArray(variable); + insn.setArray(unwrapArray().variable); insn.setReceiver(result); pe.addInstruction(insn); return pe.var(result, ValueType.INTEGER); @@ -731,4 +874,36 @@ public class ValueEmitter { phi.phi.getIncomings().add(incoming); return pe; } + + public ValueEmitter box() { + if (!(type instanceof ValueType.Primitive)) { + throw new EmitException("Can't box non-primitive: " + type); + } + + String className = getPrimitiveClassName(((ValueType.Primitive) type).getKind()); + return pe.invoke(className, "valueOf", ValueType.object(className), this); + } + + private String getPrimitiveClassName(PrimitiveType type) { + switch (type) { + case BOOLEAN: + return "java.lang.Boolean"; + case BYTE: + return "java.lang.Byte"; + case SHORT: + return "java.lang.Short"; + case CHARACTER: + return "java.lang.Character"; + case INTEGER: + return "java.lang.Integer"; + case LONG: + return "java.lang.Long"; + case FLOAT: + return "java.lang.Float"; + case DOUBLE: + return "java.lang.Double"; + default: + throw new AssertionError("Unexpected primitive type: " + type); + } + } } 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 8904fb4ea..e6fe9dab5 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 @@ -39,7 +39,7 @@ class MetadataProviderTransformer implements ClassHolderTransformer { for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) { AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName()); if (providerAnnot != null) { - transformMetadataMethod(cls, method, diagnostics); + transformMetadataMethod(cls, method, diagnostics, innerSource); } providerAnnot = method.getAnnotations().get(ClassScopedMetadataProvider.class.getName()); if (providerAnnot != null) { @@ -62,7 +62,8 @@ class MetadataProviderTransformer implements ClassHolderTransformer { } } - private void transformMetadataMethod(ClassHolder cls, MethodHolder method, Diagnostics diagnostics) { + private void transformMetadataMethod(ClassHolder cls, MethodHolder method, Diagnostics diagnostics, + ClassReaderSource classSource) { if (!validate(method, diagnostics)) { return; } @@ -89,15 +90,17 @@ class MetadataProviderTransformer implements ClassHolderTransformer { createMethod.getAnnotations().add(refAnnot); method.getModifiers().remove(ElementModifier.NATIVE); - ProgramEmitter pe = ProgramEmitter.create(method); + ProgramEmitter pe = ProgramEmitter.create(method, classSource); ForkEmitter fork = pe.getField(field.getReference(), field.getType()).fork( BinaryBranchingCondition.REFERENCE_NOT_EQUAL, pe.constantNull(field.getType())); - BasicBlock resourceFound = pe.createBlock(); + BasicBlock resourceFound = pe.prepareBlock(); fork.setThen(resourceFound); pe.getField(field.getReference(), field.getType()).returnValue(); - fork.setElse(pe.createBlock()); + BasicBlock block = pe.prepareBlock(); + fork.setElse(block); + pe.enter(block); pe.setField(field.getReference(), pe.invoke(createMethod.getReference().getClassName(), createMethod.getReference().getName(), createMethod.getResultType())); pe.jump(resourceFound);