From 453016706170d5bb56a892b42d62b9b15696ab0f Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 28 Apr 2018 19:40:08 +0300 Subject: [PATCH] C backend: implement support for simple cases of resources --- .../impl/DeclaringClassMetadataGenerator.java | 10 +- .../java/org/teavm/backend/c/CTarget.java | 18 +- .../c/IntrinsicFactoryContextImpl.java | 72 +++++++ .../java/org/teavm/backend/c/TeaVMCHost.java | 6 +- .../backend/c/generate/ClassGenerator.java | 10 + .../c/generate/CodeGenerationVisitor.java | 5 + .../backend/c/generate/GenerationContext.java | 4 + .../backend/c/intrinsic/IntrinsicContext.java | 3 + .../backend/c/intrinsic/IntrinsicFactory.java | 20 ++ .../c/intrinsic/IntrinsicFactoryContext.java | 35 ++++ .../ClassScopedMetadataGenerator.java | 6 +- .../metadata/MetadataGeneratorContext.java | 7 +- .../plugin/BuildTimeResourceProxyBuilder.java | 8 +- .../plugin/BuildTimeResourceProxyFactory.java | 8 +- ...ScopedMetadataProviderNativeGenerator.java | 3 +- .../DefaultMetadataGeneratorContext.java | 20 +- .../platform/plugin/MetadataCIntrinsic.java | 177 ++++++++++++++++++ .../teavm/platform/plugin/PlatformPlugin.java | 22 +++ .../plugin/ResourceReadCIntrinsic.java | 55 ++++++ .../platform/plugin/StringAmplifier.java | 2 + 20 files changed, 470 insertions(+), 21 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/c/IntrinsicFactoryContextImpl.java create mode 100644 core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicFactory.java create mode 100644 core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicFactoryContext.java create mode 100644 platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java create mode 100644 platform/src/main/java/org/teavm/platform/plugin/ResourceReadCIntrinsic.java diff --git a/classlib/src/main/java/org/teavm/classlib/impl/DeclaringClassMetadataGenerator.java b/classlib/src/main/java/org/teavm/classlib/impl/DeclaringClassMetadataGenerator.java index fea05dbec..0ea23c999 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/DeclaringClassMetadataGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/DeclaringClassMetadataGenerator.java @@ -15,17 +15,21 @@ */ package org.teavm.classlib.impl; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.teavm.model.ClassReader; import org.teavm.model.MethodReference; -import org.teavm.platform.metadata.*; +import org.teavm.platform.metadata.ClassScopedMetadataGenerator; +import org.teavm.platform.metadata.MetadataGeneratorContext; +import org.teavm.platform.metadata.Resource; public class DeclaringClassMetadataGenerator implements ClassScopedMetadataGenerator { @Override - public Map generateMetadata(MetadataGeneratorContext context, MethodReference method) { + public Map generateMetadata(MetadataGeneratorContext context, + Collection classNames, MethodReference method) { Map result = new HashMap<>(); - for (String clsName : context.getClassSource().getClassNames()) { + for (String clsName : classNames) { ClassReader cls = context.getClassSource().get(clsName); if (cls.getOwnerName() != null) { result.put(clsName, context.createClassResource(cls.getOwnerName())); diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index ad9df6902..7e4b8dbec 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -47,6 +47,7 @@ import org.teavm.backend.c.intrinsic.ExceptionHandlingIntrinsic; import org.teavm.backend.c.intrinsic.FunctionIntrinsic; import org.teavm.backend.c.intrinsic.GCIntrinsic; import org.teavm.backend.c.intrinsic.Intrinsic; +import org.teavm.backend.c.intrinsic.IntrinsicFactory; import org.teavm.backend.c.intrinsic.MutatorIntrinsic; import org.teavm.backend.c.intrinsic.PlatformClassIntrinsic; import org.teavm.backend.c.intrinsic.PlatformClassMetadataIntrinsic; @@ -100,7 +101,7 @@ import org.teavm.vm.TeaVMTarget; import org.teavm.vm.TeaVMTargetController; import org.teavm.vm.spi.TeaVMHostExtension; -public class CTarget implements TeaVMTarget { +public class CTarget implements TeaVMTarget, TeaVMCHost { private TeaVMTargetController controller; private ClassInitializerInsertionTransformer clinitInsertionTransformer; private ClassInitializerEliminator classInitializerEliminator; @@ -110,6 +111,7 @@ public class CTarget implements TeaVMTarget { private NullCheckTransformation nullCheckTransformation; private ExportDependencyListener exportDependencyListener = new ExportDependencyListener(); private int minHeapSize = 32 * 1024 * 1024; + private List intrinsicFactories = new ArrayList<>(); public void setMinHeapSize(int minHeapSize) { this.minHeapSize = minHeapSize; @@ -142,7 +144,12 @@ public class CTarget implements TeaVMTarget { @Override public List getHostExtensions() { - return Collections.emptyList(); + return Collections.singletonList(this); + } + + @Override + public void addIntrinsic(IntrinsicFactory intrinsicFactory) { + intrinsicFactories.add(intrinsicFactory); } @Override @@ -236,6 +243,13 @@ public class CTarget implements TeaVMTarget { ClassGenerator classGenerator = new ClassGenerator(context, controller.getUnprocessedClassSource(), tagRegistry, decompiler, codeWriter); + IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl( + classGenerator.getStructuresWriter(), classGenerator.getPreCodeWriter(), + controller.getUnprocessedClassSource(), controller.getClassLoader(), controller.getServices(), + controller.getProperties()); + for (IntrinsicFactory intrinsicFactory : intrinsicFactories) { + context.addIntrinsic(intrinsicFactory.createIntrinsic(intrinsicFactoryContext)); + } generateClasses(classes, classGenerator); generateSpecialFunctions(context, codeWriter); diff --git a/core/src/main/java/org/teavm/backend/c/IntrinsicFactoryContextImpl.java b/core/src/main/java/org/teavm/backend/c/IntrinsicFactoryContextImpl.java new file mode 100644 index 000000000..d1ac743a2 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/IntrinsicFactoryContextImpl.java @@ -0,0 +1,72 @@ +/* + * 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; + +import java.util.Properties; +import org.teavm.backend.c.generate.CodeWriter; +import org.teavm.backend.c.intrinsic.IntrinsicFactoryContext; +import org.teavm.common.ServiceRepository; +import org.teavm.model.ClassReaderSource; + +class IntrinsicFactoryContextImpl implements IntrinsicFactoryContext { + private CodeWriter structureCodeWriter; + private CodeWriter staticFieldsInitWriter; + private ClassReaderSource classSource; + private ClassLoader classLoader; + private ServiceRepository services; + private Properties properties; + + IntrinsicFactoryContextImpl(CodeWriter structureCodeWriter, CodeWriter staticFieldsInitWriter, + ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, + Properties properties) { + this.structureCodeWriter = structureCodeWriter; + this.staticFieldsInitWriter = staticFieldsInitWriter; + this.classSource = classSource; + this.classLoader = classLoader; + this.services = services; + this.properties = properties; + } + + @Override + public CodeWriter getStructureCodeWriter() { + return structureCodeWriter; + } + + @Override + public CodeWriter getStaticFieldsInitWriter() { + return staticFieldsInitWriter; + } + + @Override + public ClassReaderSource getClassSource() { + return classSource; + } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + + @Override + public ServiceRepository getServices() { + return services; + } + + @Override + public Properties getProperties() { + return properties; + } +} diff --git a/core/src/main/java/org/teavm/backend/c/TeaVMCHost.java b/core/src/main/java/org/teavm/backend/c/TeaVMCHost.java index a1abe5ccd..853bf717c 100644 --- a/core/src/main/java/org/teavm/backend/c/TeaVMCHost.java +++ b/core/src/main/java/org/teavm/backend/c/TeaVMCHost.java @@ -15,5 +15,9 @@ */ package org.teavm.backend.c; -public class TeaVMCHost { +import org.teavm.backend.c.intrinsic.IntrinsicFactory; +import org.teavm.vm.spi.TeaVMHostExtension; + +public interface TeaVMCHost extends TeaVMHostExtension { + void addIntrinsic(IntrinsicFactory intrinsicFactory); } 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 e00045e29..62fba9b81 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 @@ -74,6 +74,7 @@ public class ClassGenerator { private CodeWriter isSupertypeWriter; private CodeWriter staticGcRootsWriter; private CodeWriter callSiteWriter; + private CodeWriter preCodeWriter; private CodeWriter codeWriter; private CodeWriter staticFieldInitWriter; @@ -95,6 +96,7 @@ public class ClassGenerator { isSupertypeWriter = writer.fragment(); staticGcRootsWriter = writer.fragment(); callSiteWriter = writer.fragment(); + preCodeWriter = writer.fragment(); codeWriter = writer.fragment(); writer.println("static void initStaticFields() {").indent(); @@ -104,6 +106,14 @@ public class ClassGenerator { codeGenerator = new CodeGenerator(context, codeWriter, includes); } + public CodeWriter getPreCodeWriter() { + return preCodeWriter; + } + + public CodeWriter getStructuresWriter() { + return structuresWriter; + } + public void generateClass(ClassHolder cls) { generateClassStructure(cls); generateClassMethods(cls); 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 9d8c3314d..04e0fae56 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 @@ -693,5 +693,10 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { public MethodReference getCallingMethod() { return callingMethod; } + + @Override + public StringPool getStringPool() { + return context.getStringPool(); + } }; } diff --git a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java index 161868010..3b2572518 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java +++ b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java @@ -51,6 +51,10 @@ public class GenerationContext { this.generators = new ArrayList<>(generators); } + public void addIntrinsic(Intrinsic intrinsic) { + intrinsics.add(intrinsic); + } + public VirtualTableProvider getVirtualTableProvider() { return virtualTableProvider; } 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 bddd69048..795a82856 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 @@ -18,6 +18,7 @@ package org.teavm.backend.c.intrinsic; import org.teavm.ast.Expr; import org.teavm.backend.c.generate.CodeWriter; import org.teavm.backend.c.generate.NameProvider; +import org.teavm.backend.c.generate.StringPool; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.MethodReference; @@ -31,4 +32,6 @@ public interface IntrinsicContext { Diagnostics getDiagnotics(); MethodReference getCallingMethod(); + + StringPool getStringPool(); } diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicFactory.java b/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicFactory.java new file mode 100644 index 000000000..897562616 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicFactory.java @@ -0,0 +1,20 @@ +/* + * 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.intrinsic; + +public interface IntrinsicFactory { + Intrinsic createIntrinsic(IntrinsicFactoryContext context); +} diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicFactoryContext.java b/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicFactoryContext.java new file mode 100644 index 000000000..8265c9723 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/IntrinsicFactoryContext.java @@ -0,0 +1,35 @@ +/* + * 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.intrinsic; + +import java.util.Properties; +import org.teavm.backend.c.generate.CodeWriter; +import org.teavm.common.ServiceRepository; +import org.teavm.model.ClassReaderSource; + +public interface IntrinsicFactoryContext { + CodeWriter getStructureCodeWriter(); + + CodeWriter getStaticFieldsInitWriter(); + + ClassReaderSource getClassSource(); + + ClassLoader getClassLoader(); + + ServiceRepository getServices(); + + Properties getProperties(); +} diff --git a/platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataGenerator.java b/platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataGenerator.java index 70ecf8aa0..a825f0c59 100644 --- a/platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataGenerator.java +++ b/platform/src/main/java/org/teavm/platform/metadata/ClassScopedMetadataGenerator.java @@ -15,6 +15,7 @@ */ package org.teavm.platform.metadata; +import java.util.Collection; import java.util.Map; import org.teavm.model.MethodReference; import org.teavm.platform.PlatformClass; @@ -23,7 +24,7 @@ import org.teavm.platform.PlatformClass; *

Behaviour of this class is similar to {@link MetadataGenerator}. The difference is that method, marked with * {@link ClassScopedMetadataProvider} must take one argument of type {@link PlatformClass}. It will * return different resource for each given class, corresponding to map entries, produced by - * {@link #generateMetadata(MetadataGeneratorContext, MethodReference)}. + * {@link #generateMetadata(MetadataGeneratorContext, Collection, MethodReference)}. * * @see ClassScopedMetadataProvider * @see MetadataGenerator @@ -31,5 +32,6 @@ import org.teavm.platform.PlatformClass; * @author Alexey Andreev */ public interface ClassScopedMetadataGenerator { - Map generateMetadata(MetadataGeneratorContext context, MethodReference method); + Map generateMetadata(MetadataGeneratorContext context, Collection classNames, + MethodReference method); } diff --git a/platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java b/platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java index 094cf2f64..2078a92f3 100644 --- a/platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java +++ b/platform/src/main/java/org/teavm/platform/metadata/MetadataGeneratorContext.java @@ -17,9 +17,10 @@ package org.teavm.platform.metadata; import java.util.Properties; import org.teavm.common.ServiceRepository; +import org.teavm.model.ClassReaderSource; import org.teavm.model.FieldReference; -import org.teavm.model.ListableClassReaderSource; import org.teavm.platform.Platform; +import org.teavm.platform.plugin.ResourceTypeDescriptor; import org.teavm.vm.TeaVM; /** @@ -34,7 +35,7 @@ public interface MetadataGeneratorContext extends ServiceRepository { * * @return class source. */ - ListableClassReaderSource getClassSource(); + ClassReaderSource getClassSource(); /** * Gets the class loader that is used by the compiler. @@ -89,4 +90,6 @@ public interface MetadataGeneratorContext extends ServiceRepository { * @return a new resource. */ ResourceMap createResourceMap(); + + ResourceTypeDescriptor getTypeDescriptor(Class type); } diff --git a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java index 3d80269c3..f058eccce 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java +++ b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java @@ -34,7 +34,11 @@ class BuildTimeResourceProxyBuilder { } public BuildTimeResourceProxy buildProxy(Class iface) { - return factories.computeIfAbsent(iface, k -> createFactory(iface)).create(); + return getProxyFactory(iface).create(); + } + + public BuildTimeResourceProxyFactory getProxyFactory(Class iface) { + return factories.computeIfAbsent(iface, k -> createFactory(iface)); } private BuildTimeResourceProxyFactory createFactory(Class iface) { @@ -103,7 +107,7 @@ class BuildTimeResourceProxyBuilder { } } - return new BuildTimeResourceProxyFactory(methods, initialData); + return new BuildTimeResourceProxyFactory(methods, initialData, descriptor); } private int getPropertyIndex(String propertyName) { diff --git a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java index 1c810154c..176e51673 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java +++ b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java @@ -16,16 +16,18 @@ package org.teavm.platform.plugin; import java.lang.reflect.Method; -import java.util.HashMap; import java.util.Map; class BuildTimeResourceProxyFactory { - private Map methods = new HashMap<>(); + private Map methods; private Object[] initialData; + ResourceTypeDescriptor typeDescriptor; - public BuildTimeResourceProxyFactory(Map methods, Object[] initialData) { + public BuildTimeResourceProxyFactory(Map methods, Object[] initialData, + ResourceTypeDescriptor typeDescriptor) { this.methods = methods; this.initialData = initialData; + this.typeDescriptor = typeDescriptor; } BuildTimeResourceProxy create() { diff --git a/platform/src/main/java/org/teavm/platform/plugin/ClassScopedMetadataProviderNativeGenerator.java b/platform/src/main/java/org/teavm/platform/plugin/ClassScopedMetadataProviderNativeGenerator.java index 1c32d5f5b..6511b78a6 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ClassScopedMetadataProviderNativeGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ClassScopedMetadataProviderNativeGenerator.java @@ -81,7 +81,8 @@ public class ClassScopedMetadataProviderNativeGenerator implements Generator { DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(), context.getClassLoader(), context.getProperties(), context); - Map resourceMap = generator.generateMetadata(metadataContext, methodRef); + Map resourceMap = generator.generateMetadata(metadataContext, + context.getClassSource().getClassNames(), methodRef); writer.append("var p").ws().append("=").ws().append("\"" + RenderingUtil.escapeString("$$res_" + writer.getNaming().getFullNameFor(methodRef)) + "\"").append(";").softNewLine(); for (Map.Entry entry : resourceMap.entrySet()) { diff --git a/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java b/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java index ab4bb6143..b3860a68b 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java +++ b/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java @@ -18,18 +18,23 @@ package org.teavm.platform.plugin; import java.lang.reflect.Proxy; import java.util.Properties; import org.teavm.common.ServiceRepository; +import org.teavm.model.ClassReaderSource; import org.teavm.model.FieldReference; -import org.teavm.model.ListableClassReaderSource; -import org.teavm.platform.metadata.*; +import org.teavm.platform.metadata.ClassResource; +import org.teavm.platform.metadata.MetadataGeneratorContext; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; +import org.teavm.platform.metadata.StaticFieldResource; class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { - private ListableClassReaderSource classSource; + private ClassReaderSource classSource; private ClassLoader classLoader; private Properties properties; private BuildTimeResourceProxyBuilder proxyBuilder = new BuildTimeResourceProxyBuilder(); private ServiceRepository services; - DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader, + DefaultMetadataGeneratorContext(ClassReaderSource classSource, ClassLoader classLoader, Properties properties, ServiceRepository services) { this.classSource = classSource; this.classLoader = classLoader; @@ -38,7 +43,7 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { } @Override - public ListableClassReaderSource getClassSource() { + public ClassReaderSource getClassSource() { return classSource; } @@ -59,6 +64,11 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { proxyBuilder.buildProxy(resourceType))); } + @Override + public ResourceTypeDescriptor getTypeDescriptor(Class type) { + return proxyBuilder.getProxyFactory(type).typeDescriptor; + } + @Override public ResourceArray createResourceArray() { return new BuildTimeResourceArray<>(); diff --git a/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java new file mode 100644 index 000000000..c0af20909 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java @@ -0,0 +1,177 @@ +/* + * 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.platform.plugin; + +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.c.generate.CodeWriter; +import org.teavm.backend.c.intrinsic.Intrinsic; +import org.teavm.backend.c.intrinsic.IntrinsicContext; +import org.teavm.common.ServiceRepository; +import org.teavm.model.CallLocation; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; +import org.teavm.platform.metadata.MetadataGenerator; +import org.teavm.platform.metadata.MetadataProvider; +import org.teavm.platform.metadata.Resource; + +public class MetadataCIntrinsic implements Intrinsic { + private ClassReaderSource classSource; + private ClassLoader classLoader; + private Set writtenStructures = new HashSet<>(); + private Set writtenInitializers = new HashSet<>(); + private CodeWriter structuresWriter; + private CodeWriter staticFieldInitWriter; + private DefaultMetadataGeneratorContext metadataContext; + + public MetadataCIntrinsic(ClassReaderSource classSource, ClassLoader classLoader, + ServiceRepository services, Properties properties, CodeWriter structuresWriter, + CodeWriter staticFieldInitWriter) { + this.classSource = classSource; + this.classLoader = classLoader; + this.structuresWriter = structuresWriter; + this.staticFieldInitWriter = staticFieldInitWriter; + metadataContext = new DefaultMetadataGeneratorContext(classSource, classLoader, properties, services); + } + + @Override + public boolean canHandle(MethodReference methodReference) { + MethodReader method = classSource.resolve(methodReference); + if (method == null) { + return false; + } + + return method.getAnnotations().get(MetadataProvider.class.getName()) != null; + } + + @Override + public void apply(IntrinsicContext context, InvocationExpr invocation) { + writeInitializer(context, invocation); + context.writer().print(context.names().forMethod(invocation.getMethod())); + } + + private void writeInitializer(IntrinsicContext context, InvocationExpr invocation) { + MethodReference methodReference = invocation.getMethod(); + if (!writtenInitializers.add(methodReference)) { + return; + } + + MethodReader method = classSource.resolve(methodReference); + MetadataGenerator generator = MetadataUtils.createMetadataGenerator(classLoader, method, + new CallLocation(invocation.getMethod()), context.getDiagnotics()); + + String variableName = context.names().forMethod(methodReference); + staticFieldInitWriter.print("static ").printType(method.getResultType()).print(" ") + .print(variableName).print(" = "); + if (generator == null) { + staticFieldInitWriter.print("NULL"); + } else { + Resource resource = generator.generateMetadata(metadataContext, invocation.getMethod()); + writeValue(context, resource); + } + staticFieldInitWriter.println(";"); + } + + private void writeValue(IntrinsicContext context, Object value) { + if (value instanceof String) { + int stringIndex = context.getStringPool().getStringIndex((String) value); + staticFieldInitWriter.print("(stringPool + " + stringIndex + ")"); + } else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) { + writeResource(context, (ResourceTypeDescriptorProvider) value); + } else { + throw new IllegalArgumentException("Don't know how to write resource: " + value); + } + } + + private void writeResource(IntrinsicContext context, ResourceTypeDescriptorProvider resourceType) { + writeResourceStructure(context, resourceType.getDescriptor()); + + String structureName = context.names().forClass(resourceType.getDescriptor().getRootInterface().getName()); + Object[] propertyValues = resourceType.getValues(); + staticFieldInitWriter.print("&(" + structureName + ") {").indent(); + boolean first = true; + for (String propertyName : resourceType.getDescriptor().getPropertyTypes().keySet()) { + if (!first) { + staticFieldInitWriter.print(","); + } + first = false; + staticFieldInitWriter.println().print(".").print(propertyName).print(" = "); + int index = resourceType.getPropertyIndex(propertyName); + Object propertyValue = propertyValues[index]; + writeValue(context, propertyValue); + } + staticFieldInitWriter.println().outdent().print("}"); + } + + private void writeResourceStructure(IntrinsicContext context, ResourceTypeDescriptor structure) { + String className = structure.getRootInterface().getName(); + if (!writtenStructures.add(className)) { + return; + } + + for (Class propertyType : structure.getPropertyTypes().values()) { + if (Resource.class.isAssignableFrom(propertyType)) { + ResourceTypeDescriptor propertyStructure = metadataContext.getTypeDescriptor( + propertyType.asSubclass(Resource.class)); + writeResourceStructure(context, propertyStructure); + } + } + + String structureName = context.names().forClass(className); + structuresWriter.println("typedef struct " + structureName + " {").indent(); + + for (String propertyName : structure.getPropertyTypes().keySet()) { + Class propertyType = structure.getPropertyTypes().get(propertyName); + structuresWriter.println(typeToString(context, propertyType) + " " + propertyName + ";"); + } + + structuresWriter.outdent().println("} " + structureName + ";"); + } + + private String typeToString(IntrinsicContext context, Class cls) { + if (cls == boolean.class || cls == byte.class) { + return "int8_t"; + } else if (cls == short.class || cls == char.class) { + return "int16_t"; + } else if (cls == int.class) { + return "int32_t"; + } else if (cls == float.class) { + return "float"; + } else if (cls == long.class) { + return "int64_t"; + } else if (cls == double.class) { + return "double"; + } else if (Resource.class.isAssignableFrom(cls)) { + return "&" + context.names().forClass(cls.getName()); + } else if (cls == String.class) { + return "JavaObject*"; + } else { + throw new IllegalArgumentException("Don't know how to write resource type " + cls); + } + } + + static class MethodContext { + String baseName; + int suffix; + + MethodContext(String baseName) { + this.baseName = baseName; + } + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java index 68801bb82..e2d5df5ca 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java @@ -16,6 +16,9 @@ package org.teavm.platform.plugin; import org.teavm.ast.InvocationExpr; +import org.teavm.backend.c.TeaVMCHost; +import org.teavm.backend.c.intrinsic.Intrinsic; +import org.teavm.backend.c.intrinsic.IntrinsicContext; import org.teavm.backend.javascript.TeaVMJavaScriptHost; import org.teavm.backend.wasm.TeaVMWasmHost; import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; @@ -75,6 +78,25 @@ public class PlatformPlugin implements TeaVMPlugin { } }); } + + TeaVMCHost cHost = host.getExtension(TeaVMCHost.class); + if (cHost != null) { + cHost.addIntrinsic(ctx -> new MetadataCIntrinsic(ctx.getClassSource(), ctx.getClassLoader(), + ctx.getServices(), ctx.getProperties(), ctx.getStructureCodeWriter(), + ctx.getStaticFieldsInitWriter())); + cHost.addIntrinsic(ctx -> new ResourceReadCIntrinsic(ctx.getClassSource())); + cHost.addIntrinsic(ctx -> new Intrinsic() { + @Override + public boolean canHandle(MethodReference method) { + return method.getClassName().equals(StringAmplifier.class.getName()); + } + + @Override + public void apply(IntrinsicContext context, InvocationExpr invocation) { + context.emit(invocation.getArguments().get(0)); + } + }); + } } host.add(new AsyncMethodProcessor()); diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceReadCIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceReadCIntrinsic.java new file mode 100644 index 000000000..59552f1b6 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceReadCIntrinsic.java @@ -0,0 +1,55 @@ +/* + * 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.platform.plugin; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.c.intrinsic.Intrinsic; +import org.teavm.backend.c.intrinsic.IntrinsicContext; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.MethodReference; +import org.teavm.platform.metadata.Resource; + +public class ResourceReadCIntrinsic implements Intrinsic { + private ClassReaderSource classSource; + + public ResourceReadCIntrinsic(ClassReaderSource classSource) { + this.classSource = classSource; + } + + @Override + public boolean canHandle(MethodReference method) { + return classSource.isSuperType(Resource.class.getTypeName(), method.getClassName()).orElse(false); + } + + @Override + public void apply(IntrinsicContext context, InvocationExpr invocation) { + String name = invocation.getMethod().getName(); + if (name.startsWith("get")) { + name = name.substring(3); + } else if (name.startsWith("is")) { + name = name.substring(2); + } else { + throw new IllegalArgumentException(); + } + + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); + + context.writer().print("FIELD("); + context.emit(invocation.getArguments().get(0)); + context.writer().print(", ").print(context.names().forClass(invocation.getMethod().getClassName())); + context.writer().print(", ").print(name).print(")"); + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/StringAmplifier.java b/platform/src/main/java/org/teavm/platform/plugin/StringAmplifier.java index 682de40f5..51caab845 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/StringAmplifier.java +++ b/platform/src/main/java/org/teavm/platform/plugin/StringAmplifier.java @@ -16,11 +16,13 @@ package org.teavm.platform.plugin; import org.teavm.dependency.PluggableDependency; +import org.teavm.interop.Unmanaged; final class StringAmplifier { private StringAmplifier() { } @PluggableDependency(StringAmplifierDependencyPlugin.class) + @Unmanaged static native String amplify(String string); }