From c84ae57b3ad302491f51c977083af3d24a62906d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 3 Jun 2019 18:27:08 +0300 Subject: [PATCH] C: native structures --- .../backend/c/generate/ClassGenerator.java | 4 + .../c/generate/CodeGenerationVisitor.java | 60 ++++++++------- .../backend/c/intrinsic/AddressIntrinsic.java | 19 ++++- .../backend/c/intrinsic/IntrinsicContext.java | 3 + .../c/intrinsic/StructureIntrinsic.java | 14 +++- .../org/teavm/backend/c/util/InteropUtil.java | 75 +++++++++++++++++++ .../lowlevel/ExportDependencyListener.java | 2 +- .../main/java/org/teavm/interop/c/Name.java | 27 +++++++ .../main/java/org/teavm/interop/c/Native.java | 26 +++++++ 9 files changed, 196 insertions(+), 34 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/c/util/InteropUtil.java create mode 100644 interop/core/src/main/java/org/teavm/interop/c/Name.java create mode 100644 interop/core/src/main/java/org/teavm/interop/c/Native.java diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 6a7cb029f..07cf48b69 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -29,6 +29,7 @@ import org.teavm.ast.ControlFlowEntry; import org.teavm.ast.RegularMethodNode; import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.c.generators.Generator; +import org.teavm.backend.c.util.InteropUtil; import org.teavm.backend.lowlevel.generate.ClassGeneratorUtil; import org.teavm.cache.AstCacheEntry; import org.teavm.cache.AstDependencyExtractor; @@ -757,6 +758,9 @@ public class ClassGenerator { if (cls.hasModifier(ElementModifier.INTERFACE)) { return false; } + if (InteropUtil.isNative(cls)) { + return false; + } return !cls.getName().equals(Structure.class.getName()) && !cls.getName().equals(Address.class.getName()); } diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java index 3b7bcb476..5b762541b 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java @@ -67,15 +67,13 @@ import org.teavm.ast.VariableExpr; import org.teavm.ast.WhileStatement; import org.teavm.backend.c.intrinsic.Intrinsic; import org.teavm.backend.c.intrinsic.IntrinsicContext; +import org.teavm.backend.c.util.InteropUtil; import org.teavm.diagnostics.Diagnostics; import org.teavm.interop.Address; -import org.teavm.interop.c.Include; import org.teavm.interop.c.Variable; -import org.teavm.model.AnnotationContainerReader; -import org.teavm.model.AnnotationReader; -import org.teavm.model.AnnotationValue; import org.teavm.model.CallLocation; import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; import org.teavm.model.MethodReader; @@ -409,10 +407,10 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { public void visit(InvocationExpr expr) { ClassReader cls = context.getClassSource().get(expr.getMethod().getClassName()); if (cls != null) { - processInclude(cls.getAnnotations()); + InteropUtil.processInclude(cls.getAnnotations(), includes); MethodReader method = cls.getMethod(expr.getMethod().getDescriptor()); if (method != null) { - processInclude(method.getAnnotations()); + InteropUtil.processInclude(method.getAnnotations(), includes); } } @@ -690,23 +688,6 @@ 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.addInclude(includeString); - } - @Override public void visit(QualificationExpr expr) { FieldReference field = expr.getField(); @@ -724,15 +705,33 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { } pushLocation(expr.getLocation()); - includes.includeClass(field.getClassName()); - if (expr.getQualified() != null) { + printFieldRef(expr.getQualified(), field); + popLocation(expr.getLocation()); + } + + private void printFieldRef(Expr qualified, FieldReference field) { + if (qualified != null) { + ClassReader cls = context.getClassSource().get(field.getClassName()); writer.print("TEAVM_FIELD("); - expr.getQualified().acceptVisitor(this); - writer.print(", ").print(names.forClass(field.getClassName()) + ", " + names.forMemberField(field) + ")"); + qualified.acceptVisitor(this); + if (cls != null && isNative(cls)) { + InteropUtil.processInclude(cls.getAnnotations(), includes); + writer.print(", ").print(InteropUtil.getNativeName(cls)) + .print(", ").print(InteropUtil.getNativeName(cls, field.getFieldName())); + } else { + includes.includeClass(field.getClassName()); + writer.print(", ").print(names.forClass(field.getClassName())) + .print(", ").print(names.forMemberField(field)); + } + writer.print(")"); } else { + includes.includeClass(field.getClassName()); writer.print(names.forStaticField(field)); } - popLocation(expr.getLocation()); + } + + private boolean isNative(ClassReader cls) { + return context.getCharacteristics().isStructure(cls.getName()) && InteropUtil.isNative(cls); } private boolean isMonitorField(FieldReference field) { @@ -1122,6 +1121,11 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { public boolean isIncremental() { return context.isIncremental(); } + + @Override + public ClassReaderSource classes() { + return context.getClassSource(); + } }; private static CVariableType typeToCType(ValueType type) { diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/AddressIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/AddressIntrinsic.java index 5f6257537..34cd07b41 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/AddressIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/AddressIntrinsic.java @@ -17,7 +17,9 @@ package org.teavm.backend.c.intrinsic; import org.teavm.ast.InvocationExpr; import org.teavm.backend.c.util.ConstantUtil; +import org.teavm.backend.c.util.InteropUtil; import org.teavm.interop.Address; +import org.teavm.model.ClassReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -172,9 +174,20 @@ public class AddressIntrinsic implements Intrinsic { String className = ConstantUtil.getClassLiteral(context, invocation, invocation.getArguments().get(1)); context.emit(invocation.getArguments().get(2)); - context.writer().print(" * sizeof(") - .print(className != null ? context.names().forClass(className) : "**") - .print(")"); + + String structureName; + ClassReader cls = className != null ? context.classes().get(className) : null; + if (cls != null && InteropUtil.isNative(cls)) { + structureName = InteropUtil.getNativeName(cls); + InteropUtil.processInclude(cls.getAnnotations(), context.includes()); + } else if (className != null) { + structureName = context.names().forClass(className); + context.includes().includeClass(className); + } else { + structureName = "**"; + } + + context.writer().print(" * sizeof(").print(structureName).print(")"); context.writer().print(")"); } break; diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicContext.java b/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicContext.java index f5a843911..32816b013 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicContext.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicContext.java @@ -21,6 +21,7 @@ import org.teavm.backend.c.generate.IncludeManager; import org.teavm.backend.c.generate.NameProvider; import org.teavm.backend.c.generate.StringPool; import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.ClassReaderSource; import org.teavm.model.MethodReference; public interface IntrinsicContext { @@ -40,5 +41,7 @@ public interface IntrinsicContext { String escapeFileName(String name); + ClassReaderSource classes(); + boolean isIncremental(); } diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/StructureIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/StructureIntrinsic.java index 76082f4c2..9da7e8b27 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/StructureIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/StructureIntrinsic.java @@ -17,7 +17,9 @@ package org.teavm.backend.c.intrinsic; import org.teavm.ast.InvocationExpr; import org.teavm.backend.c.util.ConstantUtil; +import org.teavm.backend.c.util.InteropUtil; import org.teavm.interop.Structure; +import org.teavm.model.ClassReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; import org.teavm.model.lowlevel.Characteristics; @@ -69,8 +71,16 @@ public class StructureIntrinsic implements Intrinsic { case "add": { String className = ConstantUtil.getClassLiteral(context, invocation, invocation.getArguments().get(0)); if (className != null) { - context.writer().print("TEAVM_STRUCTURE_ADD(").print(context.names().forClass(className)) - .print(", "); + ClassReader cls = context.classes().get(className); + String structureName; + if (InteropUtil.isNative(cls)) { + structureName = InteropUtil.getNativeName(cls); + InteropUtil.processInclude(cls.getAnnotations(), context.includes()); + } else { + structureName = context.names().forClass(className); + context.includes().includeClass(className); + } + context.writer().print("TEAVM_STRUCTURE_ADD(").print(structureName).print(", "); context.emit(invocation.getArguments().get(1)); context.writer().print(", "); context.emit(invocation.getArguments().get(2)); diff --git a/core/src/main/java/org/teavm/backend/c/util/InteropUtil.java b/core/src/main/java/org/teavm/backend/c/util/InteropUtil.java new file mode 100644 index 000000000..5f18bcdf3 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/util/InteropUtil.java @@ -0,0 +1,75 @@ +/* + * Copyright 2019 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.backend.c.generate.IncludeManager; +import org.teavm.interop.c.Include; +import org.teavm.interop.c.Name; +import org.teavm.interop.c.Native; +import org.teavm.model.AnnotationContainerReader; +import org.teavm.model.AnnotationReader; +import org.teavm.model.AnnotationValue; +import org.teavm.model.ClassReader; +import org.teavm.model.FieldReader; + +public final class InteropUtil { + private InteropUtil() { + } + + public static boolean isNative(ClassReader cls) { + return cls.getAnnotations().get(Native.class.getName()) != null; + } + + + public static String getNativeName(ClassReader cls) { + AnnotationReader nameAnnot = cls.getAnnotations().get(Name.class.getName()); + if (nameAnnot != null) { + return nameAnnot.getValue("value").getString(); + } + + int index = Math.max(cls.getName().lastIndexOf('.'), cls.getName().lastIndexOf('$')); + return cls.getName().substring(index + 1); + } + + public static String getNativeName(ClassReader cls, String fieldName) { + FieldReader field = cls.getField(fieldName); + if (field != null) { + AnnotationReader nameAnnot = field.getAnnotations().get(Name.class.getName()); + if (nameAnnot != null) { + return nameAnnot.getValue("value").getString(); + } + } + + return fieldName; + } + + public static void processInclude(AnnotationContainerReader container, IncludeManager includes) { + 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.addInclude(includeString); + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/ExportDependencyListener.java b/core/src/main/java/org/teavm/model/lowlevel/ExportDependencyListener.java index c037ea2a2..0033f5dfb 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/ExportDependencyListener.java +++ b/core/src/main/java/org/teavm/model/lowlevel/ExportDependencyListener.java @@ -159,7 +159,7 @@ public class ExportDependencyListener extends AbstractDependencyListener { .collect(Collectors.toList()); if (candidates.isEmpty()) { diagnostics.error(location, "There's no static method '" + methodName + "' in class '{{c0}}'", - targetClass); + targetClass.getName()); valid = false; } diff --git a/interop/core/src/main/java/org/teavm/interop/c/Name.java b/interop/core/src/main/java/org/teavm/interop/c/Name.java new file mode 100644 index 000000000..06ac8900f --- /dev/null +++ b/interop/core/src/main/java/org/teavm/interop/c/Name.java @@ -0,0 +1,27 @@ +/* + * Copyright 2019 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.FIELD }) +public @interface Name { + String value(); +} diff --git a/interop/core/src/main/java/org/teavm/interop/c/Native.java b/interop/core/src/main/java/org/teavm/interop/c/Native.java new file mode 100644 index 000000000..5c92584f2 --- /dev/null +++ b/interop/core/src/main/java/org/teavm/interop/c/Native.java @@ -0,0 +1,26 @@ +/* + * Copyright 2019 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) +public @interface Native { +}