mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C backend: major improvements to interop
1. Function.get that allows to convert static Java method to a C function pointer 2. Address.ofData that gets address (void*) of Java array data 3. `@Include` annotation to add `#include` to generated C when using some method or class
This commit is contained in:
parent
38267980fb
commit
fa07100024
|
@ -4,14 +4,15 @@
|
|||
<option name="configuration">
|
||||
<map>
|
||||
<entry key="active-configuration" value="LOCAL_FILE:$PRJ_DIR$/checkstyle.xml:TeaVM" />
|
||||
<entry key="checkstyle-version" value="7.8.1" />
|
||||
<entry key="location-0" value="LOCAL_FILE:$PRJ_DIR$/checkstyle.xml:TeaVM" />
|
||||
<entry key="location-1" value="CLASSPATH:/sun_checks.xml:The default Checkstyle rules" />
|
||||
<entry key="property-0.config_loc" value="." />
|
||||
<entry key="checkstyle-version" value="7.8.2" />
|
||||
<entry key="copy-libs" value="false" />
|
||||
<entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
|
||||
<entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
|
||||
<entry key="location-2" value="LOCAL_FILE:$PRJ_DIR$/checkstyle.xml:TeaVM" />
|
||||
<entry key="property-2.config_loc" value="." />
|
||||
<entry key="scan-before-checkin" value="false" />
|
||||
<entry key="scanscope" value="JavaOnlyWithTests" />
|
||||
<entry key="suppress-errors" value="false" />
|
||||
<entry key="thirdparty-classpath" value="" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
|
|
|
@ -93,15 +93,17 @@ public class Decompiler {
|
|||
private Deque<Block> stack;
|
||||
private Program program;
|
||||
private boolean friendlyToDebugger;
|
||||
private boolean moveConstants;
|
||||
|
||||
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, Set<MethodReference> asyncMethods,
|
||||
Set<MethodReference> asyncFamilyMethods, boolean friendlyToDebugger) {
|
||||
Set<MethodReference> asyncFamilyMethods, boolean friendlyToDebugger, boolean moveConstants) {
|
||||
this.classSource = classSource;
|
||||
this.classLoader = classLoader;
|
||||
this.asyncMethods = asyncMethods;
|
||||
splitMethods.addAll(asyncMethods);
|
||||
splitMethods.addAll(asyncFamilyMethods);
|
||||
this.friendlyToDebugger = friendlyToDebugger;
|
||||
this.moveConstants = moveConstants;
|
||||
}
|
||||
|
||||
public MethodNodeCache getRegularMethodCache() {
|
||||
|
@ -283,7 +285,7 @@ public class Decompiler {
|
|||
methodNode.getVariables().add(variable);
|
||||
}
|
||||
|
||||
Optimizer optimizer = new Optimizer();
|
||||
Optimizer optimizer = new Optimizer(moveConstants);
|
||||
optimizer.optimize(methodNode, method.getProgram(), friendlyToDebugger);
|
||||
methodNode.getModifiers().addAll(method.getModifiers());
|
||||
|
||||
|
@ -347,7 +349,7 @@ public class Decompiler {
|
|||
node.getVariables().add(variable);
|
||||
}
|
||||
|
||||
Optimizer optimizer = new Optimizer();
|
||||
Optimizer optimizer = new Optimizer(moveConstants);
|
||||
optimizer.optimize(node, splitter, friendlyToDebugger);
|
||||
node.getModifiers().addAll(method.getModifiers());
|
||||
|
||||
|
|
|
@ -18,9 +18,7 @@ package org.teavm.ast.optimization;
|
|||
import java.util.BitSet;
|
||||
import org.teavm.ast.AsyncMethodNode;
|
||||
import org.teavm.ast.AsyncMethodPart;
|
||||
import org.teavm.ast.MethodNode;
|
||||
import org.teavm.ast.RegularMethodNode;
|
||||
import org.teavm.ast.VariableNode;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.Instruction;
|
||||
|
@ -33,6 +31,12 @@ import org.teavm.model.util.ProgramUtils;
|
|||
import org.teavm.model.util.UsageExtractor;
|
||||
|
||||
public class Optimizer {
|
||||
private boolean moveConstants;
|
||||
|
||||
public Optimizer(boolean moveConstants) {
|
||||
this.moveConstants = moveConstants;
|
||||
}
|
||||
|
||||
public void optimize(RegularMethodNode method, Program program, boolean friendlyToDebugger) {
|
||||
ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size());
|
||||
stats.analyze(program);
|
||||
|
@ -40,7 +44,7 @@ public class Optimizer {
|
|||
BreakEliminator breakEliminator = new BreakEliminator();
|
||||
breakEliminator.eliminate(method.getBody());
|
||||
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads,
|
||||
friendlyToDebugger);
|
||||
moveConstants ? stats.constants : new Object[stats.constants.length], friendlyToDebugger);
|
||||
method.getBody().acceptVisitor(optimizer);
|
||||
method.setBody(optimizer.resultStmt);
|
||||
int paramCount = method.getReference().parameterCount();
|
||||
|
@ -74,7 +78,7 @@ public class Optimizer {
|
|||
breakEliminator.eliminate(part.getStatement());
|
||||
findEscapingLiveVars(liveness, cfg, splitter, i, preservedVars);
|
||||
OptimizingVisitor optimizer = new OptimizingVisitor(preservedVars, stats.writes, stats.reads,
|
||||
friendlyToDebugger);
|
||||
moveConstants ? stats.constants : new Object[stats.constants.length], friendlyToDebugger);
|
||||
part.getStatement().acceptVisitor(optimizer);
|
||||
part.setStatement(optimizer.resultStmt);
|
||||
}
|
||||
|
@ -97,14 +101,6 @@ public class Optimizer {
|
|||
}
|
||||
}
|
||||
|
||||
private void preserveDebuggableVars(boolean[] variablesToPreserve, MethodNode methodNode) {
|
||||
for (VariableNode varNode : methodNode.getVariables()) {
|
||||
if (varNode.getName() != null) {
|
||||
variablesToPreserve[varNode.getIndex()] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void findEscapingLiveVars(LivenessAnalyzer liveness, Graph cfg, AsyncProgramSplitter splitter,
|
||||
int partIndex, boolean[] output) {
|
||||
Program originalProgram = splitter.getOriginalProgram();
|
||||
|
|
|
@ -69,17 +69,19 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
private final boolean[] preservedVars;
|
||||
private final int[] writeFrequencies;
|
||||
private final int[] readFrequencies;
|
||||
private final Object[] constants;
|
||||
private List<Statement> resultSequence;
|
||||
private boolean friendlyToDebugger;
|
||||
private TextLocation currentLocation;
|
||||
private Deque<TextLocation> locationStack = new LinkedList<>();
|
||||
private Deque<TextLocation> notNullLocationStack = new ArrayDeque<>();
|
||||
|
||||
OptimizingVisitor(boolean[] preservedVars, int[] writeFrequencies, int[] readFrequencies,
|
||||
OptimizingVisitor(boolean[] preservedVars, int[] writeFrequencies, int[] readFrequencies, Object[] constants,
|
||||
boolean friendlyToDebugger) {
|
||||
this.preservedVars = preservedVars;
|
||||
this.writeFrequencies = writeFrequencies;
|
||||
this.readFrequencies = readFrequencies;
|
||||
this.constants = constants;
|
||||
this.friendlyToDebugger = friendlyToDebugger;
|
||||
}
|
||||
|
||||
|
@ -273,6 +275,15 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
if (writeFrequencies[index] != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (constants[index] != null) {
|
||||
ConstantExpr constantExpr = new ConstantExpr();
|
||||
constantExpr.setValue(constants[index]);
|
||||
constantExpr.setLocation(expr.getLocation());
|
||||
resultExpr = constantExpr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (readFrequencies[index] != 1 || preservedVars[index]) {
|
||||
return;
|
||||
}
|
||||
|
@ -542,6 +553,12 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
|
|||
if (!(statement.getLeftValue() instanceof VariableExpr)) {
|
||||
statement.getLeftValue().acceptVisitor(this);
|
||||
left = resultExpr;
|
||||
} else {
|
||||
int varIndex = ((VariableExpr) statement.getLeftValue()).getIndex();
|
||||
if (writeFrequencies[varIndex] == 1 && constants[varIndex] != null) {
|
||||
resultStmt = new SequentialStatement();
|
||||
return;
|
||||
}
|
||||
}
|
||||
statement.setLeftValue(left);
|
||||
statement.setRightValue(right);
|
||||
|
|
|
@ -20,6 +20,8 @@ import org.teavm.common.Graph;
|
|||
import org.teavm.common.GraphUtils;
|
||||
import org.teavm.common.IntegerStack;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||
import org.teavm.model.instructions.StringConstantInstruction;
|
||||
import org.teavm.model.util.DefinitionExtractor;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.model.util.UsageExtractor;
|
||||
|
@ -27,6 +29,7 @@ import org.teavm.model.util.UsageExtractor;
|
|||
class ReadWriteStatsBuilder {
|
||||
public int[] reads;
|
||||
public int[] writes;
|
||||
public Object[] constants;
|
||||
|
||||
private ReadWriteStatsBuilder() {
|
||||
}
|
||||
|
@ -34,6 +37,7 @@ class ReadWriteStatsBuilder {
|
|||
public ReadWriteStatsBuilder(int variableCount) {
|
||||
reads = new int[variableCount];
|
||||
writes = new int[variableCount];
|
||||
constants = new Object[variableCount];
|
||||
}
|
||||
|
||||
public ReadWriteStatsBuilder copy() {
|
||||
|
@ -68,6 +72,14 @@ class ReadWriteStatsBuilder {
|
|||
for (Variable var : useExtractor.getUsedVariables()) {
|
||||
reads[var.getIndex()]++;
|
||||
}
|
||||
|
||||
if (insn instanceof StringConstantInstruction) {
|
||||
StringConstantInstruction stringConstant = (StringConstantInstruction) insn;
|
||||
constants[stringConstant.getReceiver().getIndex()] = stringConstant.getConstant();
|
||||
} else if (insn instanceof ClassConstantInstruction) {
|
||||
ClassConstantInstruction classConstant = (ClassConstantInstruction) insn;
|
||||
constants[classConstant.getReceiver().getIndex()] = classConstant.getConstant();
|
||||
}
|
||||
}
|
||||
|
||||
for (Phi phi : block.getPhis()) {
|
||||
|
|
|
@ -83,6 +83,7 @@ import org.teavm.model.instructions.InvokeInstruction;
|
|||
import org.teavm.model.lowlevel.Characteristics;
|
||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||
import org.teavm.model.lowlevel.ExportDependencyListener;
|
||||
import org.teavm.model.lowlevel.NullCheckInsertion;
|
||||
import org.teavm.model.lowlevel.NullCheckTransformation;
|
||||
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
||||
|
@ -107,6 +108,7 @@ public class CTarget implements TeaVMTarget {
|
|||
private ShadowStackTransformer shadowStackTransformer;
|
||||
private NullCheckInsertion nullCheckInsertion;
|
||||
private NullCheckTransformation nullCheckTransformation;
|
||||
private ExportDependencyListener exportDependencyListener = new ExportDependencyListener();
|
||||
private int minHeapSize = 32 * 1024 * 1024;
|
||||
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
|
@ -123,7 +125,7 @@ public class CTarget implements TeaVMTarget {
|
|||
|
||||
@Override
|
||||
public List<DependencyListener> getDependencyListeners() {
|
||||
return Collections.singletonList(new CDependencyListener());
|
||||
return Arrays.asList(new CDependencyListener(), exportDependencyListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -204,7 +206,7 @@ public class CTarget implements TeaVMTarget {
|
|||
StringPool stringPool = new StringPool();
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
||||
new HashSet<>(), false);
|
||||
new HashSet<>(), false, true);
|
||||
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||
|
||||
NameProvider nameProvider = new NameProvider(controller.getUnprocessedClassSource());
|
||||
|
@ -221,7 +223,7 @@ public class CTarget implements TeaVMTarget {
|
|||
intrinsics.add(new GCIntrinsic());
|
||||
intrinsics.add(new MutatorIntrinsic());
|
||||
intrinsics.add(new ExceptionHandlingIntrinsic());
|
||||
intrinsics.add(new FunctionIntrinsic(characteristics));
|
||||
intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods()));
|
||||
|
||||
List<Generator> generators = new ArrayList<>();
|
||||
generators.add(new ArrayGenerator());
|
||||
|
|
|
@ -62,6 +62,8 @@ public class ClassGenerator {
|
|||
private List<FieldReference[]> layouts = new ArrayList<>();
|
||||
private int currentLayoutIndex;
|
||||
private Set<ValueType> types = new LinkedHashSet<>();
|
||||
private Set<String> includes = new LinkedHashSet<>();
|
||||
private CodeWriter includesWriter;
|
||||
private CodeWriter forwardDeclarationsWriter;
|
||||
private CodeWriter structuresWriter;
|
||||
private CodeWriter vtableStructuresWriter;
|
||||
|
@ -81,6 +83,7 @@ public class ClassGenerator {
|
|||
this.tagRegistry = tagRegistry;
|
||||
this.decompiler = decompiler;
|
||||
|
||||
includesWriter = writer.fragment();
|
||||
forwardDeclarationsWriter = writer.fragment();
|
||||
structuresWriter = writer.fragment();
|
||||
vtableStructuresWriter = writer.fragment();
|
||||
|
@ -93,7 +96,7 @@ public class ClassGenerator {
|
|||
callSiteWriter = writer.fragment();
|
||||
codeWriter = writer.fragment();
|
||||
|
||||
codeGenerator = new CodeGenerator(context, codeWriter);
|
||||
codeGenerator = new CodeGenerator(context, codeWriter, includes);
|
||||
}
|
||||
|
||||
public void generateClass(ClassHolder cls) {
|
||||
|
@ -113,6 +116,10 @@ public class ClassGenerator {
|
|||
generateLayoutArray();
|
||||
|
||||
new StringPoolGenerator(stringPoolWriter, context.getNames()).generate(context.getStringPool().getStrings());
|
||||
|
||||
for (String include : includes) {
|
||||
includesWriter.println("#include " + include);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<ValueType> getTypes() {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.backend.c.generate;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.ArrayType;
|
||||
import org.teavm.ast.AssignmentStatement;
|
||||
import org.teavm.ast.BinaryExpr;
|
||||
|
@ -57,6 +58,11 @@ import org.teavm.backend.c.intrinsic.Intrinsic;
|
|||
import org.teavm.backend.c.intrinsic.IntrinsicContext;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.c.Include;
|
||||
import org.teavm.model.AnnotationContainerReader;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
@ -80,11 +86,13 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
private int temporaryReceiverLevel;
|
||||
private int maxTemporaryReceiverLevel;
|
||||
private MethodReference callingMethod;
|
||||
private Set<? super String> includes;
|
||||
|
||||
public CodeGenerationVisitor(GenerationContext context, CodeWriter writer) {
|
||||
public CodeGenerationVisitor(GenerationContext context, CodeWriter writer, Set<? super String> includes) {
|
||||
this.context = context;
|
||||
this.writer = writer;
|
||||
this.names = context.getNames();
|
||||
this.includes = includes;
|
||||
}
|
||||
|
||||
public int getTemporaryReceivers() {
|
||||
|
@ -371,6 +379,15 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(InvocationExpr expr) {
|
||||
ClassReader cls = context.getClassSource().get(expr.getMethod().getClassName());
|
||||
if (cls != null) {
|
||||
processInclude(cls.getAnnotations());
|
||||
MethodReader method = cls.getMethod(expr.getMethod().getDescriptor());
|
||||
if (method != null) {
|
||||
processInclude(method.getAnnotations());
|
||||
}
|
||||
}
|
||||
|
||||
Intrinsic intrinsic = context.getIntrinsic(expr.getMethod());
|
||||
if (intrinsic != null) {
|
||||
intrinsic.apply(intrinsicContext, expr);
|
||||
|
@ -439,6 +456,23 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
private void processInclude(AnnotationContainerReader container) {
|
||||
AnnotationReader annot = container.get(Include.class.getName());
|
||||
if (annot == null) {
|
||||
return;
|
||||
}
|
||||
String includeString = annot.getValue("value").getString();
|
||||
|
||||
AnnotationValue systemValue = annot.getValue("isSystem");
|
||||
if (systemValue == null || systemValue.getBoolean()) {
|
||||
includeString = "<" + includeString + ">";
|
||||
} else {
|
||||
includeString = "\"" + includeString + "\"";
|
||||
}
|
||||
|
||||
includes.add(includeString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(QualificationExpr expr) {
|
||||
if (expr.getQualified() != null) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.backend.c.generate;
|
||||
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.RegularMethodNode;
|
||||
import org.teavm.ast.VariableNode;
|
||||
import org.teavm.model.ElementModifier;
|
||||
|
@ -26,11 +27,13 @@ public class CodeGenerator {
|
|||
private CodeWriter writer;
|
||||
private CodeWriter localsWriter;
|
||||
private NameProvider names;
|
||||
private Set<? super String> includes;
|
||||
|
||||
public CodeGenerator(GenerationContext context, CodeWriter writer) {
|
||||
public CodeGenerator(GenerationContext context, CodeWriter writer, Set<? super String> includes) {
|
||||
this.context = context;
|
||||
this.writer = writer;
|
||||
this.names = context.getNames();
|
||||
this.includes = includes;
|
||||
}
|
||||
|
||||
public void generateMethod(RegularMethodNode methodNode) {
|
||||
|
@ -41,7 +44,7 @@ public class CodeGenerator {
|
|||
|
||||
localsWriter = writer.fragment();
|
||||
|
||||
CodeGenerationVisitor visitor = new CodeGenerationVisitor(context, writer);
|
||||
CodeGenerationVisitor visitor = new CodeGenerationVisitor(context, writer, includes);
|
||||
visitor.setCallingMethod(methodNode.getReference());
|
||||
methodNode.getBody().acceptVisitor(visitor);
|
||||
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
package org.teavm.backend.c.intrinsic;
|
||||
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.c.util.ConstantUtil;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class AddressIntrinsic implements Intrinsic {
|
||||
@Override
|
||||
|
@ -56,6 +58,8 @@ public class AddressIntrinsic implements Intrinsic {
|
|||
case "isLessThan":
|
||||
case "align":
|
||||
case "sizeOf":
|
||||
|
||||
case "ofData":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -165,7 +169,7 @@ public class AddressIntrinsic implements Intrinsic {
|
|||
context.writer().print("ADDRESS_ADD(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(", ");
|
||||
String className = StructureIntrinsic.getClassLiteral(context, invocation,
|
||||
String className = ConstantUtil.getClassLiteral(context, invocation,
|
||||
invocation.getArguments().get(1));
|
||||
context.emit(invocation.getArguments().get(2));
|
||||
context.writer().print(" * sizeof(")
|
||||
|
@ -191,6 +195,14 @@ public class AddressIntrinsic implements Intrinsic {
|
|||
case "sizeOf":
|
||||
context.writer().print("sizeof(void*)");
|
||||
break;
|
||||
case "ofData": {
|
||||
ValueType.Array type = (ValueType.Array) invocation.getMethod().parameterType(0);
|
||||
context.writer().print("((char*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(" + sizeof(JavaArray) + (intptr_t) ALIGN(NULL, "
|
||||
+ sizeOf(type.getItemType()) + "))");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,4 +219,22 @@ public class AddressIntrinsic implements Intrinsic {
|
|||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
}
|
||||
|
||||
private int sizeOf(ValueType type) {
|
||||
switch (((ValueType.Primitive) type).getKind()) {
|
||||
case BYTE:
|
||||
return 1;
|
||||
case SHORT:
|
||||
return 2;
|
||||
case INTEGER:
|
||||
case FLOAT:
|
||||
return 4;
|
||||
case LONG:
|
||||
case DOUBLE:
|
||||
return 8;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,25 +15,41 @@
|
|||
*/
|
||||
package org.teavm.backend.c.intrinsic;
|
||||
|
||||
import java.util.Map;
|
||||
import org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.interop.Function;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.lowlevel.Characteristics;
|
||||
import org.teavm.model.lowlevel.ExportedMethodKey;
|
||||
|
||||
public class FunctionIntrinsic implements Intrinsic {
|
||||
private Characteristics characteristics;
|
||||
private Map<? extends ExportedMethodKey, ? extends MethodReference> resolvedMethods;
|
||||
|
||||
public FunctionIntrinsic(Characteristics characteristics) {
|
||||
public FunctionIntrinsic(Characteristics characteristics,
|
||||
Map<? extends ExportedMethodKey, ? extends MethodReference> resolvedMethods) {
|
||||
this.characteristics = characteristics;
|
||||
this.resolvedMethods = resolvedMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (method.getClassName().equals(Function.class.getName()) && method.getName().equals("get")) {
|
||||
return true;
|
||||
}
|
||||
return characteristics.isFunction(method.getClassName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
MethodReference method = invocation.getMethod();
|
||||
if (method.getClassName().equals(Function.class.getName())) {
|
||||
generateGetFunction(context, invocation);
|
||||
return;
|
||||
}
|
||||
|
||||
context.writer().print("(((").printType(method.getReturnType()).print(" (*)(");
|
||||
if (method.parameterCount() > 0) {
|
||||
context.writer().printType(method.parameterType(0));
|
||||
|
@ -54,4 +70,32 @@ public class FunctionIntrinsic implements Intrinsic {
|
|||
}
|
||||
context.writer().print("))");
|
||||
}
|
||||
|
||||
private void generateGetFunction(IntrinsicContext context, InvocationExpr invocation) {
|
||||
if (!(invocation.getArguments().get(0) instanceof ConstantExpr)
|
||||
|| !(invocation.getArguments().get(1) instanceof ConstantExpr)
|
||||
|| !(invocation.getArguments().get(2) instanceof ConstantExpr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object functionClassValue = ((ConstantExpr) invocation.getArguments().get(0)).getValue();
|
||||
Object classValue = ((ConstantExpr) invocation.getArguments().get(1)).getValue();
|
||||
Object methodValue = ((ConstantExpr) invocation.getArguments().get(2)).getValue();
|
||||
if (!(functionClassValue instanceof ValueType.Object)
|
||||
|| !(classValue instanceof ValueType.Object)
|
||||
|| !(methodValue instanceof String)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String functionClassName = ((ValueType.Object) functionClassValue).getClassName();
|
||||
String className = ((ValueType.Object) classValue).getClassName();
|
||||
String methodName = (String) methodValue;
|
||||
ExportedMethodKey key = new ExportedMethodKey(functionClassName, className, methodName);
|
||||
MethodReference method = resolvedMethods.get(key);
|
||||
if (method == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.writer().print("&").print(context.names().forMethod(method));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,9 @@
|
|||
*/
|
||||
package org.teavm.backend.c.intrinsic;
|
||||
|
||||
import org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.Expr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.c.util.ConstantUtil;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.lowlevel.Characteristics;
|
||||
|
@ -62,14 +60,14 @@ public class StructureIntrinsic implements Intrinsic {
|
|||
context.emit(invocation.getArguments().get(0));
|
||||
break;
|
||||
case "sizeOf": {
|
||||
String className = getClassLiteral(context, invocation, invocation.getArguments().get(0));
|
||||
String className = ConstantUtil.getClassLiteral(context, invocation, invocation.getArguments().get(0));
|
||||
if (className != null) {
|
||||
context.writer().print("sizeof(").print(context.names().forClass(className)).print(")");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "add": {
|
||||
String className = getClassLiteral(context, invocation, invocation.getArguments().get(0));
|
||||
String className = ConstantUtil.getClassLiteral(context, invocation, invocation.getArguments().get(0));
|
||||
if (className != null) {
|
||||
context.writer().print("STRUCTURE_ADD(").print(context.names().forClass(className)).print(", ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
|
@ -81,17 +79,4 @@ public class StructureIntrinsic implements Intrinsic {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String getClassLiteral(IntrinsicContext context, InvocationExpr invocation, Expr expr) {
|
||||
if (expr instanceof ConstantExpr) {
|
||||
Object cst = ((ConstantExpr) expr).getValue();
|
||||
if (cst instanceof ValueType.Object) {
|
||||
return ((ValueType.Object) cst).getClassName();
|
||||
}
|
||||
}
|
||||
context.getDiagnotics().error(
|
||||
new CallLocation(context.getCallingMethod(), invocation.getLocation()),
|
||||
"This method should take class literal");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.c.util;
|
||||
|
||||
import org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.Expr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.c.intrinsic.IntrinsicContext;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public final class ConstantUtil {
|
||||
private ConstantUtil() {
|
||||
}
|
||||
|
||||
public static String getClassLiteral(IntrinsicContext context, InvocationExpr invocation, Expr expr) {
|
||||
if (expr instanceof ConstantExpr) {
|
||||
Object cst = ((ConstantExpr) expr).getValue();
|
||||
if (cst instanceof ValueType.Object) {
|
||||
return ((ValueType.Object) cst).getClassName();
|
||||
}
|
||||
}
|
||||
context.getDiagnotics().error(
|
||||
new CallLocation(context.getCallingMethod(), invocation.getLocation()),
|
||||
"This method should take class literal");
|
||||
return "java.lang.Object";
|
||||
}
|
||||
|
||||
public static String getStringLiteral(IntrinsicContext context, InvocationExpr invocation, Expr expr) {
|
||||
if (expr instanceof ConstantExpr) {
|
||||
Object cst = ((ConstantExpr) expr).getValue();
|
||||
if (cst instanceof String) {
|
||||
return (String) cst;
|
||||
}
|
||||
}
|
||||
context.getDiagnotics().error(
|
||||
new CallLocation(context.getCallingMethod(), invocation.getLocation()),
|
||||
"This method should take string literal");
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -313,7 +313,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
asyncFamilyMethods.addAll(asyncFinder.getAsyncFamilyMethods());
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), asyncMethods, asyncFamilyMethods,
|
||||
controller.isFriendlyToDebugger());
|
||||
controller.isFriendlyToDebugger(), false);
|
||||
decompiler.setRegularMethodCache(controller.isIncremental() ? astCache : null);
|
||||
|
||||
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
|
||||
|
|
146
core/src/main/java/org/teavm/backend/wasm/GtkExample.java
Normal file
146
core/src/main/java/org/teavm/backend/wasm/GtkExample.java
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.Function;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.interop.c.Include;
|
||||
|
||||
public final class GtkExample {
|
||||
private static Set<Object> pinnedObjects = new HashSet<>();
|
||||
|
||||
private GtkExample() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Gtk.init(0, null);
|
||||
Gtk.Window window = Gtk.windowNew(Gtk.WINDOW_TOPLEVEL);
|
||||
|
||||
CString deleteEventString = new CString("delete-event");
|
||||
CString destroyString = new CString("destroy");
|
||||
GLib.signalConnect(window, deleteEventString.data,
|
||||
Function.get(GLib.Callback.class, GtkExample.class, "windowDeleted"), null);
|
||||
GLib.signalConnect(window, destroyString.data,
|
||||
Function.get(GLib.Callback.class, GtkExample.class, "destroy"), null);
|
||||
deleteEventString.dispose();
|
||||
destroyString.dispose();
|
||||
|
||||
Gtk.setBorderWidth(window, 10);
|
||||
|
||||
CString helloWorld = new CString("Hello, world");
|
||||
Gtk.Button button = Gtk.buttonNewWithLabel(helloWorld.data);
|
||||
helloWorld.dispose();
|
||||
|
||||
CString clickedString = new CString("clicked");
|
||||
GLib.signalConnect(button, clickedString.data,
|
||||
Function.get(GLib.Callback.class, GtkExample.class, "hello"), null);
|
||||
|
||||
Gtk.add(window, button);
|
||||
Gtk.show(button);
|
||||
|
||||
Gtk.show(window);
|
||||
Gtk.main();
|
||||
}
|
||||
|
||||
private static void hello(Gtk.Widget widget, Address data) {
|
||||
System.out.println("Hello, world!");
|
||||
}
|
||||
|
||||
private static boolean windowDeleted(Gtk.Widget widget, Address event, Address data) {
|
||||
System.out.println("System event occurred");
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void destroy(Gtk.Widget widget, Address data) {
|
||||
Gtk.mainQuit();
|
||||
}
|
||||
|
||||
@Include("gtk/gtk.h")
|
||||
static class Gtk {
|
||||
static class Widget extends GLib.GObject {
|
||||
}
|
||||
|
||||
static class Window extends Container {
|
||||
}
|
||||
|
||||
static class Container extends Widget {
|
||||
}
|
||||
|
||||
static class Button extends Widget {
|
||||
}
|
||||
|
||||
@Import(name = "gtk_init")
|
||||
static native void init(int argc, Address argv);
|
||||
|
||||
@Import(name = "gtk_window_new")
|
||||
static native Window windowNew(int type);
|
||||
|
||||
@Import(name = "gtk_widget_show")
|
||||
static native void show(Widget widget);
|
||||
|
||||
@Import(name = "gtk_main")
|
||||
static native void main();
|
||||
|
||||
@Import(name = "gtk_main_quit")
|
||||
static native void mainQuit();
|
||||
|
||||
@Import(name = "gtk_container_set_border_width")
|
||||
static native void setBorderWidth(Container container, int width);
|
||||
|
||||
@Import(name = "gtk_button_new_with_label")
|
||||
static native Button buttonNewWithLabel(Address label);
|
||||
|
||||
@Import(name = "gtk_container_add")
|
||||
static native void add(Container container, Widget widget);
|
||||
|
||||
static final int WINDOW_TOPLEVEL = 0;
|
||||
static final int WINDOW_POPUP = 1;
|
||||
}
|
||||
|
||||
static class GLib {
|
||||
static class GObject extends Structure {
|
||||
}
|
||||
|
||||
@Import(name = "g_signal_connect")
|
||||
static native long signalConnect(GObject instance, Address signalName, Callback callback, Address data);
|
||||
|
||||
static abstract class Callback extends Function {
|
||||
abstract void call();
|
||||
}
|
||||
}
|
||||
|
||||
static class CString {
|
||||
final Address data;
|
||||
private final byte[] array;
|
||||
|
||||
CString(String str) {
|
||||
array = new byte[str.length() + 1];
|
||||
for (int i = 0; i < str.length(); ++i) {
|
||||
array[i] = (byte) str.charAt(i);
|
||||
}
|
||||
pinnedObjects.add(array);
|
||||
data = Address.ofData(array);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
pinnedObjects.remove(array);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -301,7 +301,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
controller.getUnprocessedClassSource(), vtableProvider, tagRegistry, binaryWriter);
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
||||
new HashSet<>(), false);
|
||||
new HashSet<>(), false, true);
|
||||
WasmStringPool stringPool = classGenerator.getStringPool();
|
||||
WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(),
|
||||
vtableProvider, tagRegistry, stringPool);
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.common.DisjointSet;
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.Function;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ProgramReader;
|
||||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.VariableReader;
|
||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
|
||||
public class ExportDependencyListener extends AbstractDependencyListener {
|
||||
private Set<MethodReference> exportedMethods = new LinkedHashSet<>();
|
||||
private Set<? extends MethodReference> readonlyExportedMethods = Collections.unmodifiableSet(exportedMethods);
|
||||
private Map<ExportedMethodKey, MethodReference> resolvedMethods = new HashMap<>();
|
||||
private Map<? extends ExportedMethodKey, ? extends MethodReference> readonlyResolvedMethods =
|
||||
Collections.unmodifiableMap(resolvedMethods);
|
||||
private Characteristics characteristics;
|
||||
|
||||
@Override
|
||||
public void started(DependencyAgent agent) {
|
||||
characteristics = new Characteristics(agent.getClassSource());
|
||||
}
|
||||
|
||||
public Set<? extends MethodReference> getExportedMethods() {
|
||||
return readonlyExportedMethods;
|
||||
}
|
||||
|
||||
public Map<? extends ExportedMethodKey, ? extends MethodReference> getResolvedMethods() {
|
||||
return readonlyResolvedMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
if (method.getMethod() == null || method.getMethod().getProgram() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProgramReader program = method.getMethod().getProgram();
|
||||
FunctionGetFinder finder = new FunctionGetFinder(program.variableCount());
|
||||
for (BasicBlockReader block : program.getBasicBlocks()) {
|
||||
block.readAllInstructions(finder);
|
||||
}
|
||||
|
||||
if (!finder.invocations.isEmpty()) {
|
||||
processInvocations(agent, method.getMethod(), finder);
|
||||
}
|
||||
}
|
||||
|
||||
private void processInvocations(DependencyAgent agent, MethodReader method, FunctionGetFinder finder) {
|
||||
int[] variableClasses = finder.variableClasses.pack(method.getProgram().variableCount());
|
||||
String[] stringConstants = new String[finder.stringConstants.length];
|
||||
ValueType[] classConstants = new ValueType[finder.classConstants.length];
|
||||
for (int i = 0; i < stringConstants.length; ++i) {
|
||||
stringConstants[variableClasses[i]] = finder.stringConstants[i];
|
||||
classConstants[variableClasses[i]] = finder.classConstants[i];
|
||||
}
|
||||
|
||||
Diagnostics diagnostics = agent.getDiagnostics();
|
||||
|
||||
for (Invocation invocation : finder.invocations) {
|
||||
ValueType functionClass = classConstants[variableClasses[invocation.functionClassVar]];
|
||||
ValueType targetClass = classConstants[variableClasses[invocation.classVar]];
|
||||
|
||||
String methodName = stringConstants[variableClasses[invocation.methodVar]];
|
||||
CallLocation location = new CallLocation(method.getReference(), invocation.location);
|
||||
|
||||
boolean valid = true;
|
||||
if (!(functionClass instanceof ValueType.Object)) {
|
||||
diagnostics.error(location, "First argument must be class literal representing "
|
||||
+ "non-array and no-primitive class");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (!(targetClass instanceof ValueType.Object)) {
|
||||
diagnostics.error(location, "Second argument must be class literal representing "
|
||||
+ "non-array and no-primitive class");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (methodName == null) {
|
||||
diagnostics.error(location, "Third argument must be string literal");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
processInvocation(agent, location, ((ValueType.Object) functionClass).getClassName(),
|
||||
((ValueType.Object) targetClass).getClassName(), methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processInvocation(DependencyAgent agent, CallLocation location, String functionClassName,
|
||||
String targetClassName, String methodName) {
|
||||
Diagnostics diagnostics = agent.getDiagnostics();
|
||||
ClassReaderSource classSource = agent.getClassSource();
|
||||
boolean valid = true;
|
||||
|
||||
ClassReader functionClass = classSource.get(functionClassName);
|
||||
if (functionClass == null) {
|
||||
diagnostics.error(location, "Class '{{c0}}' not found in class path", functionClassName);
|
||||
valid = false;
|
||||
} else if (!characteristics.isFunction(functionClassName)) {
|
||||
diagnostics.error(location, "Class '{{c0}}' does not represent a function", functionClassName);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
ClassReader targetClass = classSource.get(targetClassName);
|
||||
if (targetClass == null) {
|
||||
diagnostics.error(location, "Class '{{c0}}' not found in class path", functionClassName);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
MethodReader sam = extractSingleMethod(diagnostics, location, functionClass);
|
||||
if (sam == null) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
List<MethodReader> candidates = targetClass.getMethods().stream()
|
||||
.filter(method -> method.getName().equals(methodName) && method.hasModifier(ElementModifier.STATIC))
|
||||
.collect(Collectors.toList());
|
||||
if (candidates.isEmpty()) {
|
||||
diagnostics.error(location, "There's no static method '" + methodName + "' in class '{{c0}}'",
|
||||
targetClass);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<MethodReader> signatureCandidates = candidates.stream()
|
||||
.filter(method -> matchSignature(classSource, sam, method))
|
||||
.collect(Collectors.toList());
|
||||
if (signatureCandidates.isEmpty()) {
|
||||
if (candidates.size() == 1) {
|
||||
diagnostics.error(location, "Method '{{m0}}' does not match signature of function method '{{m1}}'",
|
||||
candidates.get(0).getReference(), sam.getReference());
|
||||
} else {
|
||||
diagnostics.error(location, "None of '" + methodName + "' methods match signature of function "
|
||||
+ "method '{{m0}}'", sam.getReference());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MethodReader resolvedMethod = findMostSpecific(diagnostics, location, classSource, signatureCandidates);
|
||||
if (resolvedMethod != null) {
|
||||
MethodReference reference = resolvedMethod.getReference();
|
||||
resolvedMethods.put(new ExportedMethodKey(functionClassName, targetClassName, methodName), reference);
|
||||
exportedMethods.add(reference);
|
||||
agent.linkMethod(reference, location).use();
|
||||
}
|
||||
}
|
||||
|
||||
private MethodReader extractSingleMethod(Diagnostics diagnostics, CallLocation location, ClassReader cls) {
|
||||
MethodReader candidate = null;
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
if (method.hasModifier(ElementModifier.STATIC) || !method.hasModifier(ElementModifier.ABSTRACT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (candidate != null) {
|
||||
diagnostics.error(location, "Function class {{c0}} must have one abstract method, it has multiple",
|
||||
cls.getName());
|
||||
return null;
|
||||
} else {
|
||||
candidate = method;
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate == null) {
|
||||
diagnostics.error(location, "Function class {{c0}} must have one abstract method, it has none",
|
||||
cls.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private MethodReader findMostSpecific(Diagnostics diagnostics, CallLocation location,
|
||||
ClassReaderSource classSource, List<MethodReader> methods) {
|
||||
MethodReader mostSpecificSoFar = methods.get(0);
|
||||
for (int i = 1; i < methods.size(); ++i) {
|
||||
MethodReader candidate = methods.get(i);
|
||||
if (matchSignature(classSource, mostSpecificSoFar, candidate)) {
|
||||
mostSpecificSoFar = candidate;
|
||||
} else if (!matchSignature(classSource, candidate, mostSpecificSoFar)) {
|
||||
diagnostics.error(location, "Ambiguous methods found for this export, examples are '{{m0}}' "
|
||||
+ "and {{m1}}", candidate, mostSpecificSoFar);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return mostSpecificSoFar;
|
||||
}
|
||||
|
||||
private boolean matchSignature(ClassReaderSource classSource, MethodReader functionMethod,
|
||||
MethodReader candidateMethod) {
|
||||
if (functionMethod.parameterCount() > candidateMethod.parameterCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < functionMethod.parameterCount(); ++i) {
|
||||
if (!classSource.isSuperType(functionMethod.parameterType(i),
|
||||
candidateMethod.parameterType(i)).orElse(false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class FunctionGetFinder extends AbstractInstructionReader {
|
||||
DisjointSet variableClasses = new DisjointSet();
|
||||
String[] stringConstants;
|
||||
ValueType[] classConstants;
|
||||
List<Invocation> invocations = new ArrayList<>();
|
||||
private TextLocation location;
|
||||
|
||||
FunctionGetFinder(int variableCount) {
|
||||
for (int i = 0; i < variableCount; ++i) {
|
||||
variableClasses.create();
|
||||
}
|
||||
stringConstants = new String[variableCount];
|
||||
classConstants = new ValueType[variableCount];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void location(TextLocation location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classConstant(VariableReader receiver, ValueType cst) {
|
||||
classConstants[receiver.getIndex()] = cst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stringConstant(VariableReader receiver, String cst) {
|
||||
stringConstants[receiver.getIndex()] = cst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assign(VariableReader receiver, VariableReader assignee) {
|
||||
variableClasses.union(receiver.getIndex(), assignee.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments, InvocationType type) {
|
||||
if (method.getClassName().equals(Function.class.getName()) && method.getName().equals("get")
|
||||
&& type == InvocationType.SPECIAL && instance == null && arguments.size() == 3) {
|
||||
Invocation invocation = new Invocation(location, arguments.get(0).getIndex(),
|
||||
arguments.get(1).getIndex(), arguments.get(2).getIndex());
|
||||
invocations.add(invocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Invocation {
|
||||
TextLocation location;
|
||||
int functionClassVar;
|
||||
int classVar;
|
||||
int methodVar;
|
||||
|
||||
Invocation(TextLocation location, int functionClassVar, int classVar, int methodVar) {
|
||||
this.location = location;
|
||||
this.functionClassVar = functionClassVar;
|
||||
this.classVar = classVar;
|
||||
this.methodVar = methodVar;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ExportedMethodKey {
|
||||
public final String functionClassName;
|
||||
public final String className;
|
||||
public final String methodName;
|
||||
|
||||
public ExportedMethodKey(String functionClassName, String className, String methodName) {
|
||||
this.functionClassName = functionClassName;
|
||||
this.className = className;
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ExportedMethodKey that = (ExportedMethodKey) o;
|
||||
return Objects.equals(functionClassName, that.functionClassName)
|
||||
&& Objects.equals(className, that.className)
|
||||
&& Objects.equals(methodName, that.methodName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(functionClassName, className, methodName);
|
||||
}
|
||||
}
|
|
@ -68,6 +68,20 @@ public final class Address {
|
|||
|
||||
public static native Address ofObject(Object obj);
|
||||
|
||||
public static native Address ofData(byte[] data);
|
||||
|
||||
public static native Address ofData(char[] data);
|
||||
|
||||
public static native Address ofData(short[] data);
|
||||
|
||||
public static native Address ofData(int[] data);
|
||||
|
||||
public static native Address ofData(long[] data);
|
||||
|
||||
public static native Address ofData(float[] data);
|
||||
|
||||
public static native Address ofData(double[] data);
|
||||
|
||||
public static native Address align(Address address, int alignment);
|
||||
|
||||
public static native int sizeOf();
|
||||
|
|
|
@ -16,4 +16,5 @@
|
|||
package org.teavm.interop;
|
||||
|
||||
public abstract class Function {
|
||||
public static native <T extends Function> T get(Class<T> functionType, Class<?> cls, String methodName);
|
||||
}
|
||||
|
|
29
interop/core/src/main/java/org/teavm/interop/c/Include.java
Normal file
29
interop/core/src/main/java/org/teavm/interop/c/Include.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.interop.c;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
public @interface Include {
|
||||
String value();
|
||||
|
||||
boolean isSystem() default true;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2018 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.interop.c;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface PlatformInt {
|
||||
}
|
Loading…
Reference in New Issue
Block a user