mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C backend: initial commit
This commit is contained in:
parent
540ad7f694
commit
05d0220dcd
|
@ -21,8 +21,6 @@
|
|||
<orderEntry type="module" module-name="teavm-samples-scala" exported="" />
|
||||
<orderEntry type="module" module-name="teavm-samples-storage" exported="" />
|
||||
<orderEntry type="module" module-name="teavm-samples-video" exported="" />
|
||||
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-runtime:1.0.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib:1.0.3" level="project" />
|
||||
</component>
|
||||
<component name="teavm">
|
||||
<option name="mainClass" value="" />
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.classlib;
|
||||
|
||||
import org.teavm.interop.PlatformMarker;
|
||||
import org.teavm.interop.PlatformMarkers;
|
||||
|
||||
public final class PlatformDetector {
|
||||
private PlatformDetector() {
|
||||
}
|
||||
|
||||
@PlatformMarker(PlatformMarkers.WEBASSEMBLY)
|
||||
public static boolean isWebAssembly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@PlatformMarker(PlatformMarkers.JAVASCRIPT)
|
||||
public static boolean isJavaScript() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@PlatformMarker(PlatformMarkers.C)
|
||||
public static boolean isC() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@PlatformMarker(PlatformMarkers.LOW_LEVEL)
|
||||
public static boolean isLowLevel() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -84,7 +84,7 @@ public class JCLPlugin implements TeaVMPlugin {
|
|||
host.registerService(ReflectionDependencyListener.class, reflection);
|
||||
host.add(reflection);
|
||||
|
||||
host.add(new PlatformMarkerSupport());
|
||||
host.add(new PlatformMarkerSupport(host.getPlatformTags()));
|
||||
}
|
||||
|
||||
TeaVMPluginUtil.handleNatives(host, Class.class);
|
||||
|
|
|
@ -17,6 +17,8 @@ package org.teavm.classlib.impl;
|
|||
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.PlatformMarker;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHolder;
|
||||
|
@ -41,6 +43,12 @@ import org.teavm.model.optimization.GlobalValueNumbering;
|
|||
import org.teavm.model.optimization.UnreachableBasicBlockElimination;
|
||||
|
||||
public class PlatformMarkerSupport implements ClassHolderTransformer {
|
||||
private String[] tags;
|
||||
|
||||
public PlatformMarkerSupport(String[] tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
|
@ -123,6 +131,20 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
|||
}
|
||||
|
||||
private boolean isMarker(MemberReader member) {
|
||||
return member.getAnnotations().get(PlatformMarker.class.getName()) != null;
|
||||
AnnotationReader annot = member.getAnnotations().get(PlatformMarker.class.getName());
|
||||
if (annot == null) {
|
||||
return false;
|
||||
}
|
||||
AnnotationValue value = annot.getValue("value");
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
String tagToMatch = value.getString();
|
||||
for (String tag : tags) {
|
||||
if (tag.equals(tagToMatch)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ import org.teavm.platform.PlatformSequence;
|
|||
import org.teavm.platform.metadata.ClassResource;
|
||||
import org.teavm.platform.metadata.ClassScopedMetadataProvider;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.runtime.RuntimeJavaObject;
|
||||
import org.teavm.runtime.RuntimeObject;
|
||||
|
||||
public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||
TString name;
|
||||
|
@ -98,7 +98,7 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
|||
return name;
|
||||
}
|
||||
|
||||
private RuntimeJavaObject getNameLowLevel() {
|
||||
private RuntimeObject getNameLowLevel() {
|
||||
RuntimeClass runtimeClass = Address.ofObject(this).toStructure();
|
||||
return runtimeClass.name;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.teavm.platform.async.AsyncCallback;
|
|||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.RuntimeArray;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.runtime.RuntimeJavaObject;
|
||||
import org.teavm.runtime.RuntimeObject;
|
||||
|
||||
@Superclass("")
|
||||
|
@ -210,17 +209,14 @@ public class TObject {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static int identityLowLevel(RuntimeJavaObject object) {
|
||||
if ((object.classReference & RuntimeObject.MONITOR_EXISTS) != 0) {
|
||||
object = (RuntimeJavaObject) object.monitor;
|
||||
}
|
||||
int result = object.monitor.toAddress().toInt();
|
||||
private static int identityLowLevel(RuntimeObject object) {
|
||||
int result = object.hashCode;
|
||||
if (result == 0) {
|
||||
result = RuntimeJavaObject.nextId++;
|
||||
result = RuntimeObject.nextId++;
|
||||
if (result == 0) {
|
||||
result = RuntimeJavaObject.nextId++;
|
||||
result = RuntimeObject.nextId++;
|
||||
}
|
||||
object.monitor = Address.fromInt(result).toStructure();
|
||||
object.hashCode = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -238,11 +234,11 @@ public class TObject {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static RuntimeJavaObject cloneLowLevel(RuntimeJavaObject self) {
|
||||
private static RuntimeObject cloneLowLevel(RuntimeObject self) {
|
||||
RuntimeClass cls = RuntimeClass.getClass(self);
|
||||
int skip = Structure.sizeOf(RuntimeJavaObject.class);
|
||||
int skip = Structure.sizeOf(RuntimeObject.class);
|
||||
int size;
|
||||
RuntimeJavaObject copy;
|
||||
RuntimeObject copy;
|
||||
if (cls.itemType == null) {
|
||||
copy = Allocator.allocate(cls).toStructure();
|
||||
size = cls.size;
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang;
|
|||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.classlib.PlatformDetector;
|
||||
import org.teavm.classlib.java.io.TConsole;
|
||||
import org.teavm.classlib.java.io.TInputStream;
|
||||
import org.teavm.classlib.java.io.TPrintStream;
|
||||
|
@ -106,11 +107,18 @@ public final class TSystem extends TObject {
|
|||
public static native long currentTimeMillis();
|
||||
|
||||
private static long currentTimeMillisLowLevel() {
|
||||
return (long) currentTimeMillisImpl();
|
||||
if (PlatformDetector.isWebAssembly()) {
|
||||
return (long) currentTimeMillisWasm();
|
||||
} else {
|
||||
return (long) currentTimeMillisC();
|
||||
}
|
||||
}
|
||||
|
||||
@Import(name = "currentTimeMillis", module = "runtime")
|
||||
private static native double currentTimeMillisImpl();
|
||||
private static native double currentTimeMillisWasm();
|
||||
|
||||
@Import(name = "currentTimeMillis")
|
||||
private static native long currentTimeMillisC();
|
||||
|
||||
private static void initPropertiesIfNeeded() {
|
||||
if (properties == null) {
|
||||
|
|
|
@ -109,7 +109,7 @@ public class TThrowable extends RuntimeException {
|
|||
private TThrowable fillInStackTraceLowLevel() {
|
||||
int stackSize = ExceptionHandling.callStackSize() - 1;
|
||||
stackTrace = new TStackTraceElement[stackSize];
|
||||
ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace, 2);
|
||||
ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -13,15 +13,17 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.generate;
|
||||
package org.teavm.ast;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public final class WasmMangling {
|
||||
private WasmMangling() {
|
||||
public final class Mangling {
|
||||
private Mangling() {
|
||||
}
|
||||
|
||||
public static String mangleIsSupertype(ValueType type) {
|
||||
|
@ -29,21 +31,55 @@ public final class WasmMangling {
|
|||
}
|
||||
|
||||
public static String mangleMethod(MethodReference method) {
|
||||
String className = method.getClassName().length() + mangleString(method.getClassName());
|
||||
String className = mangleClassBase(method.getClassName());
|
||||
StringBuilder sb = new StringBuilder("method$" + className + "_");
|
||||
String name = mangleString(method.getName());
|
||||
sb.append(mangleType(method.getReturnType()));
|
||||
sb.append(name.length() + "_" + name);
|
||||
sb.append(Arrays.stream(method.getParameterTypes())
|
||||
.map(WasmMangling::mangleType)
|
||||
.collect(Collectors.joining()));
|
||||
mangleSignature(method.getDescriptor(), sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String mangleVTableEntry(MethodDescriptor method) {
|
||||
StringBuilder sb = new StringBuilder("m_");
|
||||
mangleSignature(method, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void mangleSignature(MethodDescriptor method, StringBuilder sb) {
|
||||
sb.append(mangleType(method.getResultType()));
|
||||
sb.append(mangleString(method.getName()));
|
||||
sb.append(Arrays.stream(method.getParameterTypes())
|
||||
.map(Mangling::mangleType)
|
||||
.collect(Collectors.joining()));
|
||||
}
|
||||
|
||||
public static String mangleField(FieldReference field) {
|
||||
String className = mangleClassBase(field.getClassName());
|
||||
return "field$" + className + "_" + field.getFieldName().length() + mangleString(field.getFieldName());
|
||||
}
|
||||
|
||||
public static String mangleClass(String className) {
|
||||
return "class$" + mangleClassBase(className);
|
||||
}
|
||||
|
||||
public static String mangleClassObject(ValueType type) {
|
||||
return "tobject$" + mangleType(type);
|
||||
}
|
||||
|
||||
public static String mangleVtable(String className) {
|
||||
return "vt$" + mangleClassBase(className);
|
||||
}
|
||||
|
||||
private static String mangleClassBase(String className) {
|
||||
return className.length() + mangleString(className);
|
||||
}
|
||||
|
||||
public static String mangleInitializer(String className) {
|
||||
return "clinit$" + mangleString(className);
|
||||
}
|
||||
|
||||
public static String mangleInstanceOf(ValueType type) {
|
||||
return "instanceof$" + mangleType(type);
|
||||
}
|
||||
|
||||
private static String mangleString(String string) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < string.length(); ++i) {
|
|
@ -16,17 +16,16 @@
|
|||
package org.teavm.ast;
|
||||
|
||||
import java.util.Map;
|
||||
import org.teavm.model.instructions.ArrayElementType;
|
||||
|
||||
public class UnwrapArrayExpr extends Expr {
|
||||
private ArrayElementType elementType;
|
||||
private ArrayType elementType;
|
||||
private Expr array;
|
||||
|
||||
public UnwrapArrayExpr(ArrayElementType elementType) {
|
||||
public UnwrapArrayExpr(ArrayType elementType) {
|
||||
this.elementType = elementType;
|
||||
}
|
||||
|
||||
public ArrayElementType getElementType() {
|
||||
public ArrayType getElementType() {
|
||||
return elementType;
|
||||
}
|
||||
|
||||
|
|
|
@ -425,7 +425,7 @@ class StatementGenerator implements InstructionVisitor {
|
|||
|
||||
@Override
|
||||
public void visit(UnwrapArrayInstruction insn) {
|
||||
UnwrapArrayExpr unwrapExpr = new UnwrapArrayExpr(insn.getElementType());
|
||||
UnwrapArrayExpr unwrapExpr = new UnwrapArrayExpr(map(insn.getElementType()));
|
||||
unwrapExpr.setArray(Expr.var(insn.getArray().getIndex()));
|
||||
assign(unwrapExpr, insn.getReceiver());
|
||||
}
|
||||
|
@ -460,7 +460,7 @@ class StatementGenerator implements InstructionVisitor {
|
|||
case SHORT:
|
||||
return ArrayType.SHORT;
|
||||
case CHAR:
|
||||
return ArrayType.SHORT;
|
||||
return ArrayType.CHAR;
|
||||
case INT:
|
||||
return ArrayType.INT;
|
||||
case LONG:
|
||||
|
|
440
core/src/main/java/org/teavm/backend/c/CTarget.java
Normal file
440
core/src/main/java/org/teavm/backend/c/CTarget.java
Normal file
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* 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 com.carrotsearch.hppc.ObjectByteHashMap;
|
||||
import com.carrotsearch.hppc.ObjectByteMap;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.decompilation.Decompiler;
|
||||
import org.teavm.backend.c.analyze.CDependencyListener;
|
||||
import org.teavm.backend.c.analyze.Characteristics;
|
||||
import org.teavm.backend.c.analyze.StringPoolFiller;
|
||||
import org.teavm.backend.c.analyze.TypeCollector;
|
||||
import org.teavm.backend.c.generate.CallSiteGenerator;
|
||||
import org.teavm.backend.c.generate.ClassGenerator;
|
||||
import org.teavm.backend.c.generate.CodeWriter;
|
||||
import org.teavm.backend.c.generate.GenerationContext;
|
||||
import org.teavm.backend.c.generate.NameProvider;
|
||||
import org.teavm.backend.c.generate.StringPool;
|
||||
import org.teavm.backend.c.generate.StringPoolGenerator;
|
||||
import org.teavm.backend.c.intrinsic.AddressIntrinsic;
|
||||
import org.teavm.backend.c.intrinsic.AllocatorIntrinsic;
|
||||
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.MutatorIntrinsic;
|
||||
import org.teavm.backend.c.intrinsic.PlatformClassIntrinsic;
|
||||
import org.teavm.backend.c.intrinsic.PlatformClassMetadataIntrinsic;
|
||||
import org.teavm.backend.c.intrinsic.PlatformIntrinsic;
|
||||
import org.teavm.backend.c.intrinsic.PlatformObjectIntrinsic;
|
||||
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
|
||||
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
|
||||
import org.teavm.dependency.ClassDependency;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.PlatformMarkers;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.ListableClassHolderSource;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.classes.TagRegistry;
|
||||
import org.teavm.model.classes.VirtualTableProvider;
|
||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
||||
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
|
||||
import org.teavm.model.transformation.ClassPatch;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.ExceptionHandling;
|
||||
import org.teavm.runtime.RuntimeArray;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.runtime.RuntimeObject;
|
||||
import org.teavm.vm.BuildTarget;
|
||||
import org.teavm.vm.TeaVMEntryPoint;
|
||||
import org.teavm.vm.TeaVMTarget;
|
||||
import org.teavm.vm.TeaVMTargetController;
|
||||
import org.teavm.vm.spi.TeaVMHostExtension;
|
||||
|
||||
public class CTarget implements TeaVMTarget {
|
||||
private TeaVMTargetController controller;
|
||||
private ClassInitializerInsertionTransformer clinitInsertionTransformer;
|
||||
private ClassInitializerEliminator classInitializerEliminator;
|
||||
private ClassInitializerTransformer classInitializerTransformer;
|
||||
private ShadowStackTransformer shadowStackTransformer;
|
||||
private int minHeapSize = 32 * 1024 * 1024;
|
||||
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClassHolderTransformer> getTransformers() {
|
||||
List<ClassHolderTransformer> transformers = new ArrayList<>();
|
||||
transformers.add(new ClassPatch());
|
||||
transformers.add(new CDependencyListener());
|
||||
return transformers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DependencyListener> getDependencyListeners() {
|
||||
return Collections.singletonList(new CDependencyListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setController(TeaVMTargetController controller) {
|
||||
this.controller = controller;
|
||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||
classInitializerTransformer = new ClassInitializerTransformer();
|
||||
shadowStackTransformer = new ShadowStackTransformer(controller.getUnprocessedClassSource());
|
||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TeaVMHostExtension> getHostExtensions() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresRegisterAllocation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
|
||||
RuntimeClass.class, Address.class), null).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateArray",
|
||||
RuntimeClass.class, int.class, Address.class), null).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocateMultiArray",
|
||||
RuntimeClass.class, Address.class, int.class, RuntimeArray.class), null).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class), null).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
|
||||
Throwable.class, void.class), null).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwClassCastException",
|
||||
void.class), null).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
||||
Throwable.class), null).use();
|
||||
|
||||
dependencyAnalyzer.linkClass("java.lang.String", null);
|
||||
dependencyAnalyzer.linkClass("java.lang.Class", null);
|
||||
|
||||
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName(), null);
|
||||
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName(), null);
|
||||
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName(), null);
|
||||
for (ClassDependency classDep : Arrays.asList(runtimeClassDep, runtimeObjectDep, runtimeArrayDep)) {
|
||||
for (FieldReader field : classDep.getClassReader().getFields()) {
|
||||
dependencyAnalyzer.linkField(field.getReference(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
|
||||
clinitInsertionTransformer.apply(method, program);
|
||||
classInitializerEliminator.apply(program);
|
||||
classInitializerTransformer.transform(program);
|
||||
shadowStackTransformer.apply(program, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException {
|
||||
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
|
||||
TagRegistry tagRegistry = new TagRegistry(classes);
|
||||
StringPool stringPool = new StringPool();
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
||||
new HashSet<>(), false);
|
||||
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||
|
||||
NameProvider nameProvider = new NameProvider(controller.getUnprocessedClassSource());
|
||||
|
||||
List<Intrinsic> intrinsics = new ArrayList<>();
|
||||
intrinsics.add(new ShadowStackIntrinsic());
|
||||
intrinsics.add(new AddressIntrinsic());
|
||||
intrinsics.add(new AllocatorIntrinsic());
|
||||
intrinsics.add(new StructureIntrinsic(characteristics));
|
||||
intrinsics.add(new PlatformIntrinsic());
|
||||
intrinsics.add(new PlatformObjectIntrinsic());
|
||||
intrinsics.add(new PlatformClassIntrinsic());
|
||||
intrinsics.add(new PlatformClassMetadataIntrinsic());
|
||||
intrinsics.add(new GCIntrinsic());
|
||||
intrinsics.add(new MutatorIntrinsic());
|
||||
intrinsics.add(new ExceptionHandlingIntrinsic());
|
||||
intrinsics.add(new FunctionIntrinsic(characteristics));
|
||||
|
||||
GenerationContext context = new GenerationContext(vtableProvider, characteristics, stringPool, nameProvider,
|
||||
controller.getDiagnostics(), classes, intrinsics);
|
||||
|
||||
try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(
|
||||
buildTarget.createResource(outputName), "UTF-8"))) {
|
||||
CodeWriter codeWriter = new CodeWriter(writer);
|
||||
ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler, codeWriter);
|
||||
|
||||
copyResource(codeWriter, "runtime.c");
|
||||
TypeCollector typeCollector = new TypeCollector();
|
||||
typeCollector.collect(classes);
|
||||
typeCollector.collectFromCallSites(shadowStackTransformer.getCallSites());
|
||||
StringPoolFiller stringPoolFiller = new StringPoolFiller(stringPool);
|
||||
stringPoolFiller.fillFrom(classes);
|
||||
stringPoolFiller.fillCallSites(shadowStackTransformer.getCallSites());
|
||||
for (ValueType type : typeCollector.getTypes()) {
|
||||
stringPool.getStringIndex(ClassGenerator.nameOfType(type));
|
||||
}
|
||||
|
||||
generateClasses(classes, classGenerator, context, codeWriter, typeCollector);
|
||||
generateSpecialFunctions(context, codeWriter);
|
||||
copyResource(codeWriter, "runtime-epilogue.c");
|
||||
generateMain(context, codeWriter, classes, typeCollector);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyResource(CodeWriter writer, String resourceName) {
|
||||
ClassLoader classLoader = CTarget.class.getClassLoader();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
classLoader.getResourceAsStream("org/teavm/backend/c/" + resourceName)))) {
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
writer.println(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateClasses(ListableClassHolderSource classes, ClassGenerator classGenerator,
|
||||
GenerationContext context, CodeWriter writer, TypeCollector typeCollector) {
|
||||
List<String> classNames = sortClassNames(classes);
|
||||
|
||||
for (String className : classNames) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
classGenerator.generateForwardDeclarations(cls);
|
||||
}
|
||||
|
||||
for (String className : classNames) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
classGenerator.generateStructures(cls);
|
||||
}
|
||||
|
||||
for (String className : classNames) {
|
||||
classGenerator.generateVirtualTableStructures(classes.get(className));
|
||||
}
|
||||
|
||||
new StringPoolGenerator(writer, context.getNames()).generate(context.getStringPool().getStrings());
|
||||
|
||||
classGenerator.generateLayoutArray(classNames);
|
||||
|
||||
for (ValueType type : typeCollector.getTypes()) {
|
||||
classGenerator.generateVirtualTableForwardDeclaration(type);
|
||||
}
|
||||
for (ValueType type : typeCollector.getTypes()) {
|
||||
classGenerator.generateVirtualTable(type, typeCollector.getTypes());
|
||||
}
|
||||
|
||||
for (ValueType type : typeCollector.getTypes()) {
|
||||
classGenerator.generateIsSupertypeFunction(type);
|
||||
}
|
||||
|
||||
classGenerator.generateStaticGCRoots(classNames);
|
||||
|
||||
new CallSiteGenerator(context, writer).generate(shadowStackTransformer.getCallSites());
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
classGenerator.generateClass(cls);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> sortClassNames(ListableClassReaderSource classes) {
|
||||
List<String> classNames = new ArrayList<>(classes.getClassNames().size());
|
||||
Deque<String> stack = new ArrayDeque<>(classes.getClassNames());
|
||||
ObjectByteMap<String> stateMap = new ObjectByteHashMap<>();
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
String className = stack.pop();
|
||||
byte state = stateMap.getOrDefault(className, (byte) 0);
|
||||
switch (state) {
|
||||
case 0: {
|
||||
stateMap.put(className, (byte) 1);
|
||||
stack.push(className);
|
||||
ClassReader cls = classes.get(className);
|
||||
String parent = cls != null ? cls.getParent() : null;
|
||||
if (parent == null) {
|
||||
parent = RuntimeObject.class.getName();
|
||||
}
|
||||
if (!parent.equals(Structure.class.getName())
|
||||
&& stateMap.getOrDefault(cls.getParent(), (byte) 0) == 0) {
|
||||
stack.push(parent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
stateMap.put(className, (byte) 2);
|
||||
classNames.add(className);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return classNames;
|
||||
}
|
||||
|
||||
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
||||
Set<MethodReference> virtualMethods = new HashSet<>();
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
Program program = method.getProgram();
|
||||
if (program == null) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
for (Instruction insn : block) {
|
||||
if (insn instanceof InvokeInstruction) {
|
||||
InvokeInstruction invoke = (InvokeInstruction) insn;
|
||||
if (invoke.getType() == InvocationType.VIRTUAL) {
|
||||
virtualMethods.add(invoke.getMethod());
|
||||
}
|
||||
} else if (insn instanceof CloneArrayInstruction) {
|
||||
virtualMethods.add(new MethodReference(Object.class, "clone", Object.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new VirtualTableProvider(classes, virtualMethods);
|
||||
}
|
||||
|
||||
private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
|
||||
generateThrowCCE(context, writer);
|
||||
}
|
||||
|
||||
private void generateThrowCCE(GenerationContext context, CodeWriter writer) {
|
||||
writer.println("static void* throwClassCastException() {").indent();
|
||||
String methodName = context.getNames().forMethod(new MethodReference(ExceptionHandling.class,
|
||||
"throwClassCastException", void.class));
|
||||
writer.println(methodName + "();");
|
||||
writer.println("return NULL;");
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
|
||||
private void generateMain(GenerationContext context, CodeWriter writer, ListableClassHolderSource classes,
|
||||
TypeCollector types) {
|
||||
writer.println("int main(int argc, char** argv) {").indent();
|
||||
|
||||
writer.println("initHeap(" + minHeapSize + ");");
|
||||
generateVirtualTableHeaders(context, writer, types);
|
||||
generateStringPoolHeaders(context, writer);
|
||||
generateStaticInitializerCalls(context, writer, classes);
|
||||
generateCallToMainMethod(context, writer);
|
||||
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
|
||||
private void generateStaticInitializerCalls(GenerationContext context, CodeWriter writer,
|
||||
ListableClassReaderSource classes) {
|
||||
MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", ValueType.VOID);
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassReader cls = classes.get(className);
|
||||
if (!context.getCharacteristics().isStaticInit(cls.getName())
|
||||
&& !context.getCharacteristics().isStructure(cls.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (cls.getMethod(clinitDescriptor) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String clinitName = context.getNames().forMethod(new MethodReference(className, clinitDescriptor));
|
||||
writer.println(clinitName + "();");
|
||||
}
|
||||
}
|
||||
|
||||
private void generateVirtualTableHeaders(GenerationContext context, CodeWriter writer,
|
||||
TypeCollector typeCollector) {
|
||||
String classClassName = context.getNames().forClassInstance(ValueType.object("java.lang.Class"));
|
||||
writer.println("int32_t classHeader = PACK_CLASS(&" + classClassName + ") | " + RuntimeObject.GC_MARKED + ";");
|
||||
|
||||
for (ValueType type : typeCollector.getTypes()) {
|
||||
if (!ClassGenerator.needsVirtualTable(context.getCharacteristics(), type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String typeName = context.getNames().forClassInstance(type);
|
||||
writer.println("((JavaObject*) &" + typeName + ")->header = classHeader;");
|
||||
}
|
||||
}
|
||||
|
||||
private void generateStringPoolHeaders(GenerationContext context, CodeWriter writer) {
|
||||
String stringClassName = context.getNames().forClassInstance(ValueType.object("java.lang.String"));
|
||||
writer.println("int32_t stringHeader = PACK_CLASS(&" + stringClassName + ") | "
|
||||
+ RuntimeObject.GC_MARKED + ";");
|
||||
|
||||
int size = context.getStringPool().getStrings().size();
|
||||
writer.println("for (int i = 0; i < " + size + "; ++i) {").indent();
|
||||
writer.println("((JavaObject*) (stringPool + i))->header = stringHeader;");
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
|
||||
private void generateCallToMainMethod(GenerationContext context, CodeWriter writer) {
|
||||
TeaVMEntryPoint entryPoint = controller.getEntryPoints().get("main");
|
||||
if (entryPoint != null) {
|
||||
String mainMethod = context.getNames().forMethod(entryPoint.getReference());
|
||||
writer.println(mainMethod + "(NULL);");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlatformTags() {
|
||||
return new String[] { PlatformMarkers.C, PlatformMarkers.WEBASSEMBLY };
|
||||
}
|
||||
}
|
47
core/src/main/java/org/teavm/backend/c/Example.java
Normal file
47
core/src/main/java/org/teavm/backend/c/Example.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.math.BigInteger;
|
||||
|
||||
public final class Example {
|
||||
private Example() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Running BigInteger benchmark");
|
||||
BigInteger result = BigInteger.ONE;
|
||||
for (int j = 0; j < 100; ++j) {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
for (int k = 0; k < 5000; ++k) {
|
||||
BigInteger a = BigInteger.ZERO;
|
||||
BigInteger b = BigInteger.ONE;
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
BigInteger c = a.add(b);
|
||||
a = b;
|
||||
b = c;
|
||||
}
|
||||
result = a;
|
||||
}
|
||||
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
System.out.println("Operation took " + (end - start) + " milliseconds");
|
||||
}
|
||||
System.out.println(result.toString());
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -13,9 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.runtime;
|
||||
package org.teavm.backend.c;
|
||||
|
||||
public class RuntimeJavaObject extends RuntimeObject {
|
||||
public static int nextId = 1;
|
||||
public RuntimeObject monitor;
|
||||
public class TeaVMCHost {
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.analyze;
|
||||
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
|
||||
public class CDependencyListener extends AbstractDependencyListener implements ClassHolderTransformer {
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||
AnnotationReader delegateAnnot = method.getMethod().getAnnotations().get(DelegateTo.class.getName());
|
||||
if (delegateAnnot != null) {
|
||||
String delegateMethodName = delegateAnnot.getValue("value").getString();
|
||||
ClassReader cls = agent.getClassSource().get(method.getReference().getClassName());
|
||||
for (MethodReader delegate : cls.getMethods()) {
|
||||
if (delegate.getName().equals(delegateMethodName)) {
|
||||
if (delegate != method.getMethod()) {
|
||||
agent.linkMethod(delegate.getReference(), location).use();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
AnnotationReader delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
||||
if (delegateAnnot != null) {
|
||||
method.setProgram(null);
|
||||
method.getModifiers().add(ElementModifier.NATIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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.analyze;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectByteHashMap;
|
||||
import com.carrotsearch.hppc.ObjectByteMap;
|
||||
import org.teavm.interop.Function;
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
|
||||
public class Characteristics {
|
||||
private ClassReaderSource classSource;
|
||||
private ObjectByteMap<String> isStructure = new ObjectByteHashMap<>();
|
||||
private ObjectByteMap<String> isStaticInit = new ObjectByteHashMap<>();
|
||||
private ObjectByteMap<String> isFunction = new ObjectByteHashMap<>();
|
||||
|
||||
public Characteristics(ClassReaderSource classSource) {
|
||||
this.classSource = classSource;
|
||||
}
|
||||
|
||||
public boolean isStructure(String className) {
|
||||
byte result = isStructure.getOrDefault(className, (byte) -1);
|
||||
if (result < 0) {
|
||||
if (className.equals(Structure.class.getName())) {
|
||||
result = 1;
|
||||
} else {
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls != null && cls.getParent() != null) {
|
||||
result = isStructure(cls.getParent()) ? (byte) 1 : 0;
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
isStructure.put(className, result);
|
||||
}
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
public boolean isStaticInit(String className) {
|
||||
byte result = isStaticInit.getOrDefault(className, (byte) -1);
|
||||
if (result < 0) {
|
||||
ClassReader cls = classSource.get(className);
|
||||
result = cls != null && cls.getAnnotations().get(StaticInit.class.getName()) != null ? (byte) 1 : 0;
|
||||
isStaticInit.put(className, result);
|
||||
}
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
public boolean isFunction(String className) {
|
||||
byte result = isFunction.getOrDefault(className, (byte) -1);
|
||||
if (result < 0) {
|
||||
if (className.equals(Function.class.getName())) {
|
||||
result = 1;
|
||||
} else {
|
||||
ClassReader cls = classSource.get(className);
|
||||
if (cls != null && cls.getParent() != null) {
|
||||
result = isFunction(cls.getParent()) ? (byte) 1 : 0;
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
isFunction.put(className, result);
|
||||
}
|
||||
return result != 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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.analyze;
|
||||
|
||||
import java.util.List;
|
||||
import org.teavm.backend.c.generate.StringPool;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.ProgramReader;
|
||||
import org.teavm.model.VariableReader;
|
||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
|
||||
public class StringPoolFiller extends AbstractInstructionReader {
|
||||
private StringPool pool;
|
||||
|
||||
public StringPoolFiller(StringPool pool) {
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
public void fillFrom(ListableClassReaderSource classSource) {
|
||||
for (String className : classSource.getClassNames()) {
|
||||
addClass(classSource.get(className));
|
||||
}
|
||||
}
|
||||
|
||||
public void fillCallSites(List<CallSiteDescriptor> callSites) {
|
||||
for (CallSiteDescriptor callSite : callSites) {
|
||||
if (callSite.getLocation() != null) {
|
||||
if (callSite.getLocation().getClassName() != null) {
|
||||
pool.getStringIndex(callSite.getLocation().getClassName());
|
||||
}
|
||||
if (callSite.getLocation().getFileName() != null) {
|
||||
pool.getStringIndex(callSite.getLocation().getFileName());
|
||||
}
|
||||
if (callSite.getLocation().getMethodName() != null) {
|
||||
pool.getStringIndex(callSite.getLocation().getMethodName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addClass(ClassReader cls) {
|
||||
pool.getStringIndex(cls.getName());
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
ProgramReader program = method.getProgram();
|
||||
if (program != null) {
|
||||
for (BasicBlockReader block : program.getBasicBlocks()) {
|
||||
block.readAllInstructions(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stringConstant(VariableReader receiver, String cst) {
|
||||
pool.getStringIndex(cst);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.analyze;
|
||||
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.ast.InvocationType;
|
||||
import org.teavm.ast.RecursiveVisitor;
|
||||
|
||||
public class TemporaryVariableEstimator extends RecursiveVisitor {
|
||||
private int currentReceiverIndex;
|
||||
private int maxReceiverIndex;
|
||||
|
||||
public int getMaxReceiverIndex() {
|
||||
return maxReceiverIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvocationExpr expr) {
|
||||
if (expr.getType() == InvocationType.DYNAMIC || expr.getType() == InvocationType.CONSTRUCTOR) {
|
||||
currentReceiverIndex++;
|
||||
maxReceiverIndex = Math.max(maxReceiverIndex, currentReceiverIndex);
|
||||
}
|
||||
|
||||
super.visit(expr);
|
||||
|
||||
if (expr.getType() == InvocationType.DYNAMIC) {
|
||||
currentReceiverIndex--;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* 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.analyze;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.ProgramReader;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.VariableReader;
|
||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.ExceptionHandlerDescriptor;
|
||||
|
||||
public class TypeCollector extends AbstractInstructionReader {
|
||||
private Set<ValueType> types = new HashSet<>();
|
||||
|
||||
public Set<? extends ValueType> getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
public void collect(ListableClassReaderSource classSource) {
|
||||
for (String className : classSource.getClassNames()) {
|
||||
ClassReader cls = classSource.get(className);
|
||||
collect(cls);
|
||||
}
|
||||
}
|
||||
|
||||
public void collectFromCallSites(List<CallSiteDescriptor> callSites) {
|
||||
for (CallSiteDescriptor callSite : callSites) {
|
||||
for (ExceptionHandlerDescriptor handler : callSite.getHandlers()) {
|
||||
if (handler.getClassName() != null) {
|
||||
types.add(ValueType.object(handler.getClassName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void collect(ClassReader cls) {
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
collect(method);
|
||||
types.add(ValueType.object(cls.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
private void collect(MethodReader method) {
|
||||
if (method.getAnnotations().get(DelegateTo.class.getName()) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (method.getProgram() != null) {
|
||||
collect(method.getProgram());
|
||||
}
|
||||
}
|
||||
|
||||
private void collect(ProgramReader program) {
|
||||
for (BasicBlockReader block : program.getBasicBlocks()) {
|
||||
block.readAllInstructions(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createArray(VariableReader receiver, ValueType itemType, List<? extends VariableReader> dimensions) {
|
||||
ValueType type = itemType;
|
||||
for (int i = 1; i <= dimensions.size(); ++i) {
|
||||
type = ValueType.arrayOf(type);
|
||||
}
|
||||
addType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
|
||||
addType(ValueType.arrayOf(itemType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classConstant(VariableReader receiver, ValueType cst) {
|
||||
addType(cst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
|
||||
addType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
|
||||
addType(targetType);
|
||||
}
|
||||
|
||||
private void addType(ValueType type) {
|
||||
types.add(type);
|
||||
while (type instanceof ValueType.Array) {
|
||||
type = ((ValueType.Array) type).getItemType();
|
||||
if (!types.add(type)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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.generate;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.CallSiteLocation;
|
||||
import org.teavm.model.lowlevel.ExceptionHandlerDescriptor;
|
||||
|
||||
public class CallSiteGenerator {
|
||||
private static final String CALL_SITE = "org.teavm.runtime.CallSite";
|
||||
private static final String CALL_SITE_LOCATION = "org.teavm.runtime.CallSiteLocation";
|
||||
private static final String EXCEPTION_HANDLER = "org.teavm.runtime.ExceptionHandler";
|
||||
|
||||
private GenerationContext context;
|
||||
private CodeWriter writer;
|
||||
private ObjectIntMap<CallSiteLocation> locationMap = new ObjectIntHashMap<>();
|
||||
private List<CallSiteLocation> locations = new ArrayList<>();
|
||||
private List<ExceptionHandlerDescriptor> exceptionHandlers = new ArrayList<>();
|
||||
private String callSiteLocationName;
|
||||
private String exceptionHandlerName;
|
||||
|
||||
public CallSiteGenerator(GenerationContext context, CodeWriter writer) {
|
||||
this.context = context;
|
||||
this.writer = writer;
|
||||
callSiteLocationName = context.getNames().forClass(CALL_SITE_LOCATION);
|
||||
exceptionHandlerName = context.getNames().forClass(EXCEPTION_HANDLER);
|
||||
}
|
||||
|
||||
public void generate(List<CallSiteDescriptor> callSites) {
|
||||
writer.print("static ").print(callSiteLocationName).println(" callSiteLocations[];");
|
||||
writer.print("static ").print(exceptionHandlerName).println(" exceptionHandlers[];");
|
||||
|
||||
generateCallSites(callSites);
|
||||
generateLocations();
|
||||
generateHandlers();
|
||||
}
|
||||
|
||||
private void generateCallSites(List<CallSiteDescriptor> callSites) {
|
||||
String callSiteName = context.getNames().forClass(CALL_SITE);
|
||||
|
||||
writer.print("static ").print(callSiteName).print(" callSites[] = {").indent();
|
||||
String handlerCountName = fieldName(CALL_SITE, "handlerCount");
|
||||
String firstHandlerName = fieldName(CALL_SITE, "firstHandler");
|
||||
String locationName = fieldName(CALL_SITE, "location");
|
||||
|
||||
boolean first = true;
|
||||
for (CallSiteDescriptor callSite : callSites) {
|
||||
if (!first) {
|
||||
writer.print(", ");
|
||||
}
|
||||
first = false;
|
||||
|
||||
int locationIndex = -1;
|
||||
if (callSite.getLocation() != null) {
|
||||
locationIndex = locationMap.getOrDefault(callSite.getLocation(), -1);
|
||||
if (locationIndex < 0) {
|
||||
locationIndex = locations.size();
|
||||
locationMap.put(callSite.getLocation(), locationIndex);
|
||||
locations.add(callSite.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
String firstHandlerExpr = !callSite.getHandlers().isEmpty()
|
||||
? "exceptionHandlers + " + exceptionHandlers.size()
|
||||
: "NULL";
|
||||
writer.println().print("{ ");
|
||||
writer.print(".").print(handlerCountName).print(" = ")
|
||||
.print(String.valueOf(callSite.getHandlers().size())).print(", ");
|
||||
writer.print(".").print(firstHandlerName).print(" = ").print(firstHandlerExpr).print(", ");
|
||||
writer.print(".").print(locationName).print(" = ")
|
||||
.print(locationIndex >= 0 ? "callSiteLocations + " + locationIndex : "NULL");
|
||||
writer.print(" }");
|
||||
|
||||
exceptionHandlers.addAll(callSite.getHandlers());
|
||||
}
|
||||
|
||||
writer.println().outdent().println("};");
|
||||
}
|
||||
|
||||
private void generateLocations() {
|
||||
writer.print("static ").print(callSiteLocationName).print(" callSiteLocations[] = {").indent();
|
||||
|
||||
String fileNameName = fieldName(CALL_SITE_LOCATION, "fileName");
|
||||
String classNameName = fieldName(CALL_SITE_LOCATION, "className");
|
||||
String methodNameName = fieldName(CALL_SITE_LOCATION, "methodName");
|
||||
String lineNumberName = fieldName(CALL_SITE_LOCATION, "lineNumber");
|
||||
|
||||
boolean first = true;
|
||||
for (CallSiteLocation location : locations) {
|
||||
if (!first) {
|
||||
writer.print(",");
|
||||
}
|
||||
first = false;
|
||||
|
||||
writer.println().print("{ ");
|
||||
writer.print(".").print(fileNameName).print(" = ")
|
||||
.print(getStringExpr(location.getFileName())).print(", ");
|
||||
writer.print(".").print(classNameName).print(" = ")
|
||||
.print(getStringExpr(location.getClassName())).print(", ");
|
||||
writer.print(".").print(methodNameName).print(" = ")
|
||||
.print(getStringExpr(location.getMethodName())).print(", ");
|
||||
writer.print(".").print(lineNumberName).print(" = ")
|
||||
.print(String.valueOf(location.getLineNumber()));
|
||||
|
||||
writer.print(" }");
|
||||
}
|
||||
|
||||
writer.println().outdent().println("};");
|
||||
}
|
||||
|
||||
private void generateHandlers() {
|
||||
writer.print("static ").print(exceptionHandlerName).print(" exceptionHandlers[] = {").indent();
|
||||
|
||||
String idName = fieldName(EXCEPTION_HANDLER, "id");
|
||||
String exceptionClassName = fieldName(EXCEPTION_HANDLER, "exceptionClass");
|
||||
|
||||
boolean first = true;
|
||||
for (ExceptionHandlerDescriptor handler : exceptionHandlers) {
|
||||
if (!first) {
|
||||
writer.print(",");
|
||||
}
|
||||
first = false;
|
||||
|
||||
writer.println().print("{ ");
|
||||
|
||||
String classExpr = handler.getClassName() != null
|
||||
? "&" + context.getNames().forClassInstance(ValueType.object(handler.getClassName()))
|
||||
: "NULL";
|
||||
writer.print(".").print(idName).print(" = ").print(String.valueOf(handler.getId())).print(",");
|
||||
writer.print(".").print(exceptionClassName).print(" = ").print(classExpr);
|
||||
|
||||
writer.print(" }");
|
||||
}
|
||||
|
||||
writer.println().outdent().println("};");
|
||||
}
|
||||
|
||||
private String fieldName(String className, String fieldName) {
|
||||
return context.getNames().forMemberField(new FieldReference(className, fieldName));
|
||||
}
|
||||
|
||||
private String getStringExpr(String s) {
|
||||
return s != null ? "stringPool + " + context.getStringPool().getStringIndex(s) : "NULL";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,660 @@
|
|||
/*
|
||||
* 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.generate;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.RegularMethodNode;
|
||||
import org.teavm.ast.decompilation.Decompiler;
|
||||
import org.teavm.backend.c.analyze.Characteristics;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.classes.TagRegistry;
|
||||
import org.teavm.model.classes.VirtualTable;
|
||||
import org.teavm.model.classes.VirtualTableEntry;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.runtime.RuntimeObject;
|
||||
|
||||
public class ClassGenerator {
|
||||
private GenerationContext context;
|
||||
private Decompiler decompiler;
|
||||
private TagRegistry tagRegistry;
|
||||
private CodeWriter writer;
|
||||
private CodeGenerator codeGenerator;
|
||||
private ObjectIntMap<String> classLayoutOffsets = new ObjectIntHashMap<>();
|
||||
|
||||
public ClassGenerator(GenerationContext context, TagRegistry tagRegistry, Decompiler decompiler,
|
||||
CodeWriter writer) {
|
||||
this.context = context;
|
||||
this.tagRegistry = tagRegistry;
|
||||
this.decompiler = decompiler;
|
||||
this.writer = writer;
|
||||
codeGenerator = new CodeGenerator(context, writer);
|
||||
}
|
||||
|
||||
public void generateForwardDeclarations(ClassHolder cls) {
|
||||
generateForwardClassStructure(cls);
|
||||
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (method.hasModifier(ElementModifier.ABSTRACT)) {
|
||||
continue;
|
||||
}
|
||||
if (method.hasModifier(ElementModifier.NATIVE)
|
||||
&& method.getAnnotations().get(DelegateTo.class.getName()) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
codeGenerator.generateMethodSignature(method.getReference(), method.hasModifier(ElementModifier.STATIC),
|
||||
false);
|
||||
writer.println(";");
|
||||
}
|
||||
|
||||
if (needsInitializer(cls)) {
|
||||
writer.print("static void ").print(context.getNames().forClassInitializer(cls.getName())).println("();");
|
||||
}
|
||||
}
|
||||
|
||||
private void generateForwardClassStructure(ClassHolder cls) {
|
||||
if (!needsData(cls) || isSystemClass(cls)) {
|
||||
return;
|
||||
}
|
||||
|
||||
writer.print("struct ").print(context.getNames().forClass(cls.getName())).println(";");
|
||||
}
|
||||
|
||||
public void generateStructures(ClassHolder cls) {
|
||||
generateClassStructure(cls);
|
||||
generateStaticFields(cls);
|
||||
}
|
||||
|
||||
public void generateVirtualTableStructures(ClassHolder cls) {
|
||||
generateVirtualTableStructure(cls);
|
||||
}
|
||||
|
||||
private void generateClassStructure(ClassHolder cls) {
|
||||
if (!needsData(cls)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String name = context.getNames().forClass(cls.getName());
|
||||
|
||||
writer.print("typedef struct ").print(name).println(" {").indent();
|
||||
|
||||
if (cls.getParent() == null || !cls.getParent().equals(Structure.class.getName())) {
|
||||
String parentName = cls.getParent();
|
||||
if (parentName == null) {
|
||||
parentName = RuntimeObject.class.getName();
|
||||
}
|
||||
writer.print("struct ").print(context.getNames().forClass(parentName)).println(" parent;");
|
||||
}
|
||||
|
||||
for (FieldHolder field : cls.getFields()) {
|
||||
if (field.hasModifier(ElementModifier.STATIC)) {
|
||||
continue;
|
||||
}
|
||||
String fieldName = context.getNames().forMemberField(field.getReference());
|
||||
writer.printStrictType(field.getType()).print(" ").print(fieldName).println(";");
|
||||
}
|
||||
|
||||
writer.outdent().print("} ").print(name).println(";");
|
||||
}
|
||||
|
||||
private void generateStaticFields(ClassHolder cls) {
|
||||
if (!needsData(cls)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (FieldHolder field : cls.getFields()) {
|
||||
if (!field.hasModifier(ElementModifier.STATIC)) {
|
||||
continue;
|
||||
}
|
||||
String fieldName = context.getNames().forStaticField(field.getReference());
|
||||
writer.print("static ").printStrictType(field.getType()).print(" ").print(fieldName).println(";");
|
||||
}
|
||||
}
|
||||
|
||||
public void generateStaticGCRoots(Collection<String> classNames) {
|
||||
List<FieldReference[]> data = new ArrayList<>();
|
||||
int total = 0;
|
||||
|
||||
for (String className : classNames) {
|
||||
ClassReader cls = context.getClassSource().get(className);
|
||||
if (!needsData(cls)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FieldReference[] fields = new FieldReference[cls.getFields().size()];
|
||||
int index = 0;
|
||||
|
||||
for (FieldReader field : cls.getFields()) {
|
||||
if (!field.hasModifier(ElementModifier.STATIC) || !isReferenceType(field.getType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fields[index++] = field.getReference();
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fields = Arrays.copyOf(fields, index);
|
||||
total += fields.length;
|
||||
data.add(fields);
|
||||
}
|
||||
|
||||
writer.println("static void** gc_staticRoots[" + (total + 1) + "] = {").indent();
|
||||
writer.print("(void**) (intptr_t) " + total);
|
||||
|
||||
for (FieldReference[] fields : data) {
|
||||
writer.print(",").println();
|
||||
|
||||
boolean first = true;
|
||||
for (FieldReference field : fields) {
|
||||
if (!first) {
|
||||
writer.print(", ");
|
||||
}
|
||||
first = false;
|
||||
String name = context.getNames().forStaticField(field);
|
||||
writer.print("(void**) &").print(name);
|
||||
}
|
||||
}
|
||||
|
||||
writer.println().outdent().println("};");
|
||||
}
|
||||
|
||||
private boolean isReferenceType(ValueType type) {
|
||||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
return !context.getCharacteristics().isStructure(className)
|
||||
&& !className.equals(Address.class.getName());
|
||||
} else {
|
||||
return type instanceof ValueType.Array;
|
||||
}
|
||||
}
|
||||
|
||||
public void generateVirtualTableForwardDeclaration(ValueType type) {
|
||||
if (!needsVirtualTable(context.getCharacteristics(), type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String className;
|
||||
if (type instanceof ValueType.Object) {
|
||||
className = ((ValueType.Object) type).getClassName();
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
className = "java.lang.Object";
|
||||
} else {
|
||||
className = null;
|
||||
}
|
||||
String structName = className != null ? context.getNames().forClassClass(className) : "JavaClass";
|
||||
String name = context.getNames().forClassInstance(type);
|
||||
|
||||
writer.print("static ").print(structName).print(" ").print(name).println(";");
|
||||
}
|
||||
|
||||
private void generateVirtualTableStructure(ClassHolder cls) {
|
||||
if (!needsVirtualTable(context.getCharacteristics(), ValueType.object(cls.getName()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
String name = context.getNames().forClassClass(cls.getName());
|
||||
|
||||
writer.print("typedef struct ").print(name).println(" {").indent();
|
||||
writer.println("JavaClass parent;");
|
||||
|
||||
VirtualTable virtualTable = context.getVirtualTableProvider().lookup(cls.getName());
|
||||
CodeGenerator codeGenerator = new CodeGenerator(context, writer);
|
||||
for (VirtualTableEntry entry : virtualTable.getEntries().values()) {
|
||||
String methodName = context.getNames().forVirtualMethod(
|
||||
new MethodReference(cls.getName(), entry.getMethod()));
|
||||
writer.printType(entry.getMethod().getResultType())
|
||||
.print(" (*").print(methodName).print(")(");
|
||||
codeGenerator.generateMethodParameters(entry.getMethod(), false, false);
|
||||
writer.println(");");
|
||||
}
|
||||
|
||||
writer.outdent().print("} ").print(name).println(";");
|
||||
}
|
||||
|
||||
public void generateLayoutArray(List<String> classNames) {
|
||||
List<FieldReference[]> data = new ArrayList<>();
|
||||
int totalSize = 0;
|
||||
|
||||
for (String className : classNames) {
|
||||
ClassReader cls = context.getClassSource().get(className);
|
||||
if (!needsData(cls) || !needsVirtualTable(context.getCharacteristics(), ValueType.object(className))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FieldReference[] fields = new FieldReference[cls.getFields().size()];
|
||||
int index = 0;
|
||||
for (FieldReader field : cls.getFields()) {
|
||||
if (field.hasModifier(ElementModifier.STATIC) || !isReferenceType(field.getType())) {
|
||||
continue;
|
||||
}
|
||||
fields[index++] = field.getReference();
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fields = Arrays.copyOf(fields, index);
|
||||
|
||||
classLayoutOffsets.put(className, totalSize);
|
||||
totalSize += fields.length + 1;
|
||||
data.add(fields);
|
||||
}
|
||||
|
||||
writer.print("static int16_t classLayouts[" + totalSize + "] = {").indent();
|
||||
for (int i = 0; i < data.size(); ++i) {
|
||||
if (i > 0) {
|
||||
writer.print(",");
|
||||
}
|
||||
FieldReference[] fields = data.get(i);
|
||||
writer.println().print("INT16_C(" + fields.length + ")");
|
||||
|
||||
for (FieldReference field : fields) {
|
||||
String className = context.getNames().forClass(field.getClassName());
|
||||
String fieldName = context.getNames().forMemberField(field);
|
||||
writer.print(", (int16_t) offsetof(" + className + ", " + fieldName + ")");
|
||||
}
|
||||
}
|
||||
writer.println().outdent().println("};");
|
||||
}
|
||||
|
||||
public void generateVirtualTable(ValueType type, Set<? extends ValueType> allTypes) {
|
||||
if (!needsVirtualTable(context.getCharacteristics(), type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
generateIsSupertypeForwardDeclaration(type);
|
||||
|
||||
String className = null;
|
||||
if (type instanceof ValueType.Object) {
|
||||
className = ((ValueType.Object) type).getClassName();
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
className = "java.lang.Object";
|
||||
}
|
||||
String structName = className != null
|
||||
? context.getNames().forClassClass(className)
|
||||
: "JavaClass";
|
||||
String name = context.getNames().forClassInstance(type);
|
||||
|
||||
writer.print("static ").print(structName).print(" ").print(name).println(" = {").indent();
|
||||
|
||||
if (className != null) {
|
||||
writer.println(".parent = {").indent();
|
||||
generateRuntimeClassInitializer(type, allTypes);
|
||||
writer.outdent().println("},");
|
||||
|
||||
VirtualTable virtualTable = context.getVirtualTableProvider().lookup(className);
|
||||
if (virtualTable != null) {
|
||||
List<VirtualTableEntry> entries = new ArrayList<>(virtualTable.getEntries().values());
|
||||
for (int i = 0; i < entries.size(); ++i) {
|
||||
VirtualTableEntry entry = entries.get(i);
|
||||
String methodName = context.getNames().forVirtualMethod(
|
||||
new MethodReference(className, entry.getMethod()));
|
||||
String implName = entry.getImplementor() != null
|
||||
? "&" + context.getNames().forMethod(entry.getImplementor())
|
||||
: "NULL";
|
||||
writer.print(".").print(methodName).print(" = ").print(implName);
|
||||
if (i < entries.size() - 1) {
|
||||
writer.print(",");
|
||||
}
|
||||
writer.println();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
generateRuntimeClassInitializer(type, allTypes);
|
||||
}
|
||||
|
||||
writer.outdent().println("};");
|
||||
}
|
||||
|
||||
private void generateRuntimeClassInitializer(ValueType type, Set<? extends ValueType> allTypes) {
|
||||
String sizeExpr;
|
||||
int tag;
|
||||
String parent;
|
||||
String itemTypeExpr;
|
||||
int flags = 0;
|
||||
String layout = "NULL";
|
||||
|
||||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
ClassReader cls = context.getClassSource().get(className);
|
||||
|
||||
if (className.equals(Object.class.getName())) {
|
||||
className = RuntimeObject.class.getName();
|
||||
}
|
||||
|
||||
if (needsData(cls)) {
|
||||
String structName = context.getNames().forClass(className);
|
||||
sizeExpr = "(int32_t) (intptr_t) ALIGN(sizeof(" + structName + "), sizeof(void*))";
|
||||
} else {
|
||||
sizeExpr = "0";
|
||||
}
|
||||
tag = tagRegistry.getRanges(className).get(0).lower;
|
||||
|
||||
parent = cls != null && cls.getParent() != null && allTypes.contains(ValueType.object(cls.getParent()))
|
||||
? "&" + context.getNames().forClassInstance(ValueType.object(cls.getParent()))
|
||||
: "NULL";
|
||||
itemTypeExpr = "NULL";
|
||||
int layoutOffset = classLayoutOffsets.getOrDefault(className, -1);
|
||||
layout = layoutOffset >= 0 ? "classLayouts + " + layoutOffset : "NULL";
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
parent = "&" + context.getNames().forClassInstance(ValueType.object("java.lang.Object"));
|
||||
tag = tagRegistry.getRanges("java.lang.Object").get(0).lower;
|
||||
ValueType itemType = ((ValueType.Array) type).getItemType();
|
||||
sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(itemType) + ")";
|
||||
itemTypeExpr = "&" + context.getNames().forClassInstance(itemType);
|
||||
} else if (type == ValueType.VOID) {
|
||||
parent = "NULL";
|
||||
tag = 0;
|
||||
sizeExpr = "0";
|
||||
itemTypeExpr = "NULL";
|
||||
} else {
|
||||
parent = "NULL";
|
||||
tag = 0;
|
||||
sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(type) + ")";
|
||||
flags |= RuntimeClass.PRIMITIVE;
|
||||
itemTypeExpr = "NULL";
|
||||
}
|
||||
|
||||
int nameRef = context.getStringPool().getStringIndex(nameOfType(type));
|
||||
String superTypeFunction = context.getNames().forSupertypeFunction(type);
|
||||
|
||||
ValueType arrayType = ValueType.arrayOf(type);
|
||||
String arrayTypeExpr;
|
||||
if (allTypes.contains(arrayType)) {
|
||||
arrayTypeExpr = "&" + context.getNames().forClassInstance(arrayType);
|
||||
} else {
|
||||
arrayTypeExpr = "NULL";
|
||||
}
|
||||
|
||||
writer.println(".parent = {},");
|
||||
writer.print(".").print(classFieldName("size")).print(" = ").print(sizeExpr).println(",");
|
||||
writer.print(".").print(classFieldName("flags")).println(" = " + flags + ",");
|
||||
writer.print(".").print(classFieldName("tag")).print(" = ").print(String.valueOf(tag)).println(",");
|
||||
writer.print(".").print(classFieldName("canary")).println(" = 0,");
|
||||
writer.print(".").print(classFieldName("name")).println(" = stringPool + " + nameRef + ",");
|
||||
writer.print(".").print(classFieldName("arrayType")).println(" = " + arrayTypeExpr + ",");
|
||||
writer.print(".").print(classFieldName("itemType")).println(" = " + itemTypeExpr + ",");
|
||||
writer.print(".").print(classFieldName("isSupertypeOf")).println(" = &" + superTypeFunction + ",");
|
||||
writer.print(".").print(classFieldName("parent")).println(" = " + parent + ",");
|
||||
writer.print(".").print(classFieldName("enumValues")).println(" = NULL,");
|
||||
writer.print(".").print(classFieldName("layout")).println(" = " + layout);
|
||||
}
|
||||
|
||||
private String classFieldName(String field) {
|
||||
return context.getNames().forMemberField(new FieldReference(RuntimeClass.class.getName(), field));
|
||||
}
|
||||
|
||||
private boolean needsData(ClassReader cls) {
|
||||
if (cls.hasModifier(ElementModifier.INTERFACE)) {
|
||||
return false;
|
||||
}
|
||||
return !cls.getName().equals(Structure.class.getName())
|
||||
&& !cls.getName().equals(Address.class.getName());
|
||||
}
|
||||
|
||||
private boolean isSystemClass(ClassHolder cls) {
|
||||
switch (cls.getName()) {
|
||||
case "java.lang.Object":
|
||||
case "java.lang.Class":
|
||||
case "java.lang.String":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean needsVirtualTable(Characteristics characteristics, ValueType type) {
|
||||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
if (className.equals(Address.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
return !characteristics.isStructure(className);
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
return needsVirtualTable(characteristics, ((ValueType.Array) type).getItemType());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void generateClass(ClassHolder cls) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (method.hasModifier(ElementModifier.NATIVE)) {
|
||||
tryDelegateToMethod(cls, method);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.hasModifier(ElementModifier.ABSTRACT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RegularMethodNode methodNode = decompiler.decompileRegular(method);
|
||||
codeGenerator.generateMethod(methodNode);
|
||||
}
|
||||
|
||||
if (needsInitializer(cls)) {
|
||||
writer.print("static void ").print(context.getNames().forClassInitializer(cls.getName()))
|
||||
.println("() {").indent();
|
||||
|
||||
String classInstanceName = context.getNames().forClassInstance(ValueType.object(cls.getName()));
|
||||
String clinitName = context.getNames().forMethod(
|
||||
new MethodReference(cls.getName(), "<clinit>", ValueType.VOID));
|
||||
writer.print("JavaClass* cls = (JavaClass*) &").print(classInstanceName).println(";");
|
||||
writer.println("if (!(cls->flags & INT32_C(" + RuntimeClass.INITIALIZED + "))) {").indent();
|
||||
writer.println("cls->flags |= INT32_C(" + RuntimeClass.INITIALIZED + ");");
|
||||
writer.print(clinitName).println("();");
|
||||
writer.outdent().println("}");
|
||||
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needsInitializer(ClassHolder cls) {
|
||||
return !context.getCharacteristics().isStaticInit(cls.getName())
|
||||
&& !context.getCharacteristics().isStructure(cls.getName())
|
||||
&& cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null;
|
||||
}
|
||||
|
||||
private void tryDelegateToMethod(ClassHolder cls, MethodHolder method) {
|
||||
AnnotationHolder delegateToAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
||||
if (delegateToAnnot == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String methodName = delegateToAnnot.getValue("value").getString();
|
||||
for (MethodHolder candidate : cls.getMethods()) {
|
||||
if (candidate.getName().equals(methodName)) {
|
||||
delegateToMethod(method, candidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void delegateToMethod(MethodHolder callingMethod, MethodHolder delegateMethod) {
|
||||
codeGenerator.generateMethodSignature(callingMethod.getReference(),
|
||||
callingMethod.hasModifier(ElementModifier.STATIC), true);
|
||||
writer.println(" {").indent();
|
||||
|
||||
if (callingMethod.getResultType() != ValueType.VOID) {
|
||||
writer.print("return ");
|
||||
}
|
||||
|
||||
writer.print(context.getNames().forMethod(delegateMethod.getReference())).print("(");
|
||||
|
||||
boolean isStatic = callingMethod.hasModifier(ElementModifier.STATIC);
|
||||
int start = 0;
|
||||
if (!isStatic) {
|
||||
writer.print("_this_");
|
||||
} else {
|
||||
if (callingMethod.parameterCount() > 0) {
|
||||
writer.print("local_1");
|
||||
}
|
||||
start++;
|
||||
}
|
||||
|
||||
for (int i = start; i < callingMethod.parameterCount(); ++i) {
|
||||
writer.print(", ").print("local_").print(String.valueOf(i + 1));
|
||||
}
|
||||
|
||||
writer.println(");");
|
||||
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
|
||||
public static String nameOfType(ValueType type) {
|
||||
if (type instanceof ValueType.Primitive) {
|
||||
switch (((ValueType.Primitive) type).getKind()) {
|
||||
case BOOLEAN:
|
||||
return "boolean";
|
||||
case BYTE:
|
||||
return "byte";
|
||||
case SHORT:
|
||||
return "short";
|
||||
case CHARACTER:
|
||||
return "char";
|
||||
case INTEGER:
|
||||
return "int";
|
||||
case LONG:
|
||||
return "long";
|
||||
case FLOAT:
|
||||
return "float";
|
||||
case DOUBLE:
|
||||
return "double";
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
return nameOfType(((ValueType.Array) type).getItemType()) + "[]";
|
||||
} else if (type == ValueType.VOID) {
|
||||
return "void";
|
||||
} else if (type instanceof ValueType.Object) {
|
||||
return ((ValueType.Object) type).getClassName();
|
||||
} else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
private void generateIsSupertypeForwardDeclaration(ValueType type) {
|
||||
String name = context.getNames().forSupertypeFunction(type);
|
||||
writer.println("static int32_t " + name + "(JavaClass*);");
|
||||
}
|
||||
|
||||
public void generateIsSupertypeFunction(ValueType type) {
|
||||
String name = context.getNames().forSupertypeFunction(type);
|
||||
writer.println("static int32_t " + name + "(JavaClass* cls) {").indent();
|
||||
|
||||
if (type instanceof ValueType.Object) {
|
||||
generateIsSuperclassFunction(((ValueType.Object) type).getClassName());
|
||||
} else if (type instanceof ValueType.Primitive) {
|
||||
generateIsSuperPrimitiveFunction((ValueType.Primitive) type);
|
||||
} else if (type == ValueType.VOID) {
|
||||
generateIsSuperclassFunction("java.lang.Void");
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
generateIsSuperArrayFunction(((ValueType.Array) type).getItemType());
|
||||
}
|
||||
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
|
||||
private void generateIsSuperclassFunction(String className) {
|
||||
List<TagRegistry.Range> ranges = tagRegistry.getRanges(className);
|
||||
if (ranges.isEmpty()) {
|
||||
writer.println("return INT32_C(0);");
|
||||
return;
|
||||
}
|
||||
|
||||
String tagName = context.getNames().forMemberField(new FieldReference(
|
||||
RuntimeClass.class.getName(), "tag"));
|
||||
writer.println("int32_t tag = cls->" + tagName + ";");
|
||||
|
||||
int lower = ranges.get(0).lower;
|
||||
int upper = ranges.get(ranges.size() - 1).upper;
|
||||
writer.println("if (tag < " + lower + " || tag > " + upper + ") return INT32_C(0);");
|
||||
|
||||
for (int i = 1; i < ranges.size(); ++i) {
|
||||
lower = ranges.get(i - 1).upper;
|
||||
upper = ranges.get(i).lower;
|
||||
writer.println("if (tag < " + lower + " || tag > " + upper + ") return INT32_C(0);");
|
||||
}
|
||||
|
||||
writer.println("return INT32_C(1);");
|
||||
}
|
||||
|
||||
private void generateIsSuperArrayFunction(ValueType itemType) {
|
||||
String itemTypeName = context.getNames().forMemberField(new FieldReference(
|
||||
RuntimeClass.class.getName(), "itemType"));
|
||||
writer.println("JavaClass* itemType = cls->" + itemTypeName + ";");
|
||||
writer.println("if (itemType == NULL) return INT32_C(0);");
|
||||
|
||||
if (itemType instanceof ValueType.Primitive) {
|
||||
writer.println("return itemType == &" + context.getNames().forClassInstance(itemType) + ";");
|
||||
} else {
|
||||
writer.println("return " + context.getNames().forSupertypeFunction(itemType) + "(itemType);");
|
||||
}
|
||||
}
|
||||
|
||||
private void generateIsSuperPrimitiveFunction(ValueType.Primitive primitive) {
|
||||
switch (primitive.getKind()) {
|
||||
case BOOLEAN:
|
||||
generateIsSuperclassFunction("java.lang.Boolean");
|
||||
break;
|
||||
case BYTE:
|
||||
generateIsSuperclassFunction("java.lang.Byte");
|
||||
break;
|
||||
case SHORT:
|
||||
generateIsSuperclassFunction("java.lang.Short");
|
||||
break;
|
||||
case CHARACTER:
|
||||
generateIsSuperclassFunction("java.lang.Character");
|
||||
break;
|
||||
case INTEGER:
|
||||
generateIsSuperclassFunction("java.lang.Integer");
|
||||
break;
|
||||
case LONG:
|
||||
generateIsSuperclassFunction("java.lang.Long");
|
||||
break;
|
||||
case FLOAT:
|
||||
generateIsSuperclassFunction("java.lang.Float");
|
||||
break;
|
||||
case DOUBLE:
|
||||
generateIsSuperclassFunction("java.lang.Double");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,677 @@
|
|||
/*
|
||||
* 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.generate;
|
||||
|
||||
import java.util.List;
|
||||
import org.teavm.ast.ArrayType;
|
||||
import org.teavm.ast.AssignmentStatement;
|
||||
import org.teavm.ast.BinaryExpr;
|
||||
import org.teavm.ast.BlockStatement;
|
||||
import org.teavm.ast.BreakStatement;
|
||||
import org.teavm.ast.CastExpr;
|
||||
import org.teavm.ast.ConditionalExpr;
|
||||
import org.teavm.ast.ConditionalStatement;
|
||||
import org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.ContinueStatement;
|
||||
import org.teavm.ast.Expr;
|
||||
import org.teavm.ast.ExprVisitor;
|
||||
import org.teavm.ast.GotoPartStatement;
|
||||
import org.teavm.ast.InitClassStatement;
|
||||
import org.teavm.ast.InstanceOfExpr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.ast.MonitorEnterStatement;
|
||||
import org.teavm.ast.MonitorExitStatement;
|
||||
import org.teavm.ast.NewArrayExpr;
|
||||
import org.teavm.ast.NewExpr;
|
||||
import org.teavm.ast.NewMultiArrayExpr;
|
||||
import org.teavm.ast.OperationType;
|
||||
import org.teavm.ast.PrimitiveCastExpr;
|
||||
import org.teavm.ast.QualificationExpr;
|
||||
import org.teavm.ast.ReturnStatement;
|
||||
import org.teavm.ast.SequentialStatement;
|
||||
import org.teavm.ast.Statement;
|
||||
import org.teavm.ast.StatementVisitor;
|
||||
import org.teavm.ast.SubscriptExpr;
|
||||
import org.teavm.ast.SwitchClause;
|
||||
import org.teavm.ast.SwitchStatement;
|
||||
import org.teavm.ast.ThrowStatement;
|
||||
import org.teavm.ast.TryCatchStatement;
|
||||
import org.teavm.ast.UnaryExpr;
|
||||
import org.teavm.ast.UnwrapArrayExpr;
|
||||
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.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.ExceptionHandling;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
|
||||
public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||
private static final MethodReference ALLOC_METHOD = new MethodReference(Allocator.class,
|
||||
"allocate", RuntimeClass.class, Address.class);
|
||||
private static final MethodReference ALLOC_ARRAY_METHOD = new MethodReference(Allocator.class,
|
||||
"allocateArray", RuntimeClass.class, int.class, Address.class);
|
||||
private static final MethodReference ALLOC_MULTI_ARRAY_METHOD = new MethodReference(Allocator.class,
|
||||
"allocateMultiArray", RuntimeClass.class, Address.class, int.class, Address.class);
|
||||
private static final MethodReference THROW_EXCEPTION_METHOD = new MethodReference(ExceptionHandling.class,
|
||||
"throwException", Throwable.class, void.class);
|
||||
|
||||
private GenerationContext context;
|
||||
private NameProvider names;
|
||||
private CodeWriter writer;
|
||||
private int temporaryReceiverLevel;
|
||||
private MethodReference callingMethod;
|
||||
|
||||
public CodeGenerationVisitor(GenerationContext context, CodeWriter writer) {
|
||||
this.context = context;
|
||||
this.writer = writer;
|
||||
this.names = context.getNames();
|
||||
}
|
||||
|
||||
public void setCallingMethod(MethodReference callingMethod) {
|
||||
this.callingMethod = callingMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BinaryExpr expr) {
|
||||
switch (expr.getOperation()) {
|
||||
case COMPARE:
|
||||
writer.print("compare_");
|
||||
switch (expr.getType()) {
|
||||
case INT:
|
||||
writer.print("i32");
|
||||
break;
|
||||
case LONG:
|
||||
writer.print("i64");
|
||||
break;
|
||||
case FLOAT:
|
||||
writer.print("float");
|
||||
break;
|
||||
case DOUBLE:
|
||||
writer.print("double");
|
||||
break;
|
||||
}
|
||||
writer.print("(");
|
||||
expr.getFirstOperand().acceptVisitor(this);
|
||||
writer.print(", ");
|
||||
expr.getSecondOperand().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
return;
|
||||
case UNSIGNED_RIGHT_SHIFT: {
|
||||
String type = expr.getType() == OperationType.LONG ? "int64_t" : "int32_t";
|
||||
writer.print("((" + type + ") ((u" + type + ") ");
|
||||
|
||||
expr.getFirstOperand().acceptVisitor(this);
|
||||
writer.print(" >> ");
|
||||
expr.getSecondOperand().acceptVisitor(this);
|
||||
|
||||
writer.print("))");
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
writer.print("(");
|
||||
expr.getFirstOperand().acceptVisitor(this);
|
||||
|
||||
String op;
|
||||
switch (expr.getOperation()) {
|
||||
case ADD:
|
||||
op = "+";
|
||||
break;
|
||||
case SUBTRACT:
|
||||
op = "-";
|
||||
break;
|
||||
case MULTIPLY:
|
||||
op = "*";
|
||||
break;
|
||||
case DIVIDE:
|
||||
op = "/";
|
||||
break;
|
||||
case MODULO:
|
||||
op = "%";
|
||||
break;
|
||||
case BITWISE_AND:
|
||||
op = "&";
|
||||
break;
|
||||
case BITWISE_OR:
|
||||
op = "|";
|
||||
break;
|
||||
case BITWISE_XOR:
|
||||
op = "^";
|
||||
break;
|
||||
case LEFT_SHIFT:
|
||||
op = "<<";
|
||||
break;
|
||||
case RIGHT_SHIFT:
|
||||
op = ">>";
|
||||
break;
|
||||
case EQUALS:
|
||||
op = "==";
|
||||
break;
|
||||
case NOT_EQUALS:
|
||||
op = "!=";
|
||||
break;
|
||||
case GREATER:
|
||||
op = ">";
|
||||
break;
|
||||
case GREATER_OR_EQUALS:
|
||||
op = ">=";
|
||||
break;
|
||||
case LESS:
|
||||
op = "<";
|
||||
break;
|
||||
case LESS_OR_EQUALS:
|
||||
op = "<=";
|
||||
break;
|
||||
case AND:
|
||||
op = "&&";
|
||||
break;
|
||||
case OR:
|
||||
op = "||";
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
writer.print(" ").print(op).print(" ");
|
||||
expr.getSecondOperand().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UnaryExpr expr) {
|
||||
switch (expr.getOperation()) {
|
||||
case NOT:
|
||||
writer.print("(");
|
||||
writer.print("!");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
break;
|
||||
case NEGATE:
|
||||
writer.print("(");
|
||||
writer.print("-");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
break;
|
||||
case LENGTH:
|
||||
writer.print("ARRAY_LENGTH(");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
break;
|
||||
case NULL_CHECK:
|
||||
writer.print("(");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.print(" == NULL)");
|
||||
break;
|
||||
case INT_TO_BYTE:
|
||||
writer.print("TO_BYTE(");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
break;
|
||||
case INT_TO_SHORT:
|
||||
writer.print("TO_SHORT(");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
break;
|
||||
case INT_TO_CHAR:
|
||||
writer.print("TO_CHAR(");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ConditionalExpr expr) {
|
||||
writer.print("(");
|
||||
expr.getCondition().acceptVisitor(this);
|
||||
writer.print(" ? ");
|
||||
expr.getConsequent().acceptVisitor(this);
|
||||
writer.print(" : ");
|
||||
expr.getAlternative().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ConstantExpr expr) {
|
||||
Object value = expr.getValue();
|
||||
|
||||
if (value == null) {
|
||||
writer.print("NULL");
|
||||
} else if (value instanceof String) {
|
||||
int index = context.getStringPool().getStringIndex((String) value);
|
||||
writer.print("(stringPool + " + index + ")");
|
||||
} else if (value instanceof Integer) {
|
||||
writer.print("INT32_C(" + value + ")");
|
||||
} else if (value instanceof Long) {
|
||||
writer.print("INT64_C(" + value + ")");
|
||||
} else if (value instanceof Float) {
|
||||
writer.print(value + "f");
|
||||
} else if (value instanceof Double) {
|
||||
writer.print(value.toString());
|
||||
} else if (value instanceof Boolean) {
|
||||
writer.print((Boolean) value ? "1" : "0");
|
||||
} else if (value instanceof ValueType) {
|
||||
writer.print("&").print(names.forClassInstance((ValueType) value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(VariableExpr expr) {
|
||||
if (expr.getIndex() == 0) {
|
||||
writer.print("_this_");
|
||||
} else {
|
||||
writer.print("local_" + expr.getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SubscriptExpr expr) {
|
||||
writer.print("ARRAY_AT(");
|
||||
expr.getArray().acceptVisitor(this);
|
||||
writer.print(", ").print(getArrayType(expr.getType())).print(", ");
|
||||
expr.getIndex().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UnwrapArrayExpr expr) {
|
||||
expr.getArray().acceptVisitor(this);
|
||||
}
|
||||
|
||||
private static String getArrayType(ArrayType type) {
|
||||
switch (type) {
|
||||
case BYTE:
|
||||
return "int8_t";
|
||||
case SHORT:
|
||||
return "int16_t";
|
||||
case CHAR:
|
||||
return "char16_t";
|
||||
case INT:
|
||||
return "int32_t";
|
||||
case LONG:
|
||||
return "int64_t";
|
||||
case FLOAT:
|
||||
return "float";
|
||||
case DOUBLE:
|
||||
return "double";
|
||||
case OBJECT:
|
||||
return "void*";
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvocationExpr expr) {
|
||||
Intrinsic intrinsic = context.getIntrinsic(expr.getMethod());
|
||||
if (intrinsic != null) {
|
||||
intrinsic.apply(intrinsicContext, expr);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (expr.getType()) {
|
||||
case CONSTRUCTOR: {
|
||||
String receiver = "recv_" + temporaryReceiverLevel++;
|
||||
writer.print("(" + receiver + " = ");
|
||||
allocObject(expr.getMethod().getClassName());
|
||||
writer.print(", ");
|
||||
|
||||
MethodReader method = context.getClassSource().resolve(expr.getMethod());
|
||||
writer.print(names.forMethod(method.getReference()));
|
||||
|
||||
writer.print("(" + receiver);
|
||||
for (Expr arg : expr.getArguments()) {
|
||||
writer.print(", ");
|
||||
arg.acceptVisitor(this);
|
||||
}
|
||||
writer.print("), " + receiver + ")");
|
||||
|
||||
--temporaryReceiverLevel;
|
||||
|
||||
break;
|
||||
}
|
||||
case SPECIAL:
|
||||
case STATIC: {
|
||||
MethodReader method = context.getClassSource().resolve(expr.getMethod());
|
||||
writer.print(names.forMethod(method.getReference()));
|
||||
|
||||
writer.print("(");
|
||||
if (!expr.getArguments().isEmpty()) {
|
||||
expr.getArguments().get(0).acceptVisitor(this);
|
||||
for (int i = 1; i < expr.getArguments().size(); ++i) {
|
||||
writer.print(", ");
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
}
|
||||
}
|
||||
writer.print(")");
|
||||
|
||||
break;
|
||||
}
|
||||
case DYNAMIC: {
|
||||
String receiver = "recv_" + temporaryReceiverLevel++;
|
||||
writer.print("((").print(receiver).print(" = ");
|
||||
expr.getArguments().get(0).acceptVisitor(this);
|
||||
|
||||
writer.print("), METHOD(")
|
||||
.print(receiver).print(", ")
|
||||
.print(names.forClassClass(expr.getMethod().getClassName())).print(", ")
|
||||
.print(names.forVirtualMethod(expr.getMethod()))
|
||||
.print(")(").print(receiver);
|
||||
for (int i = 1; i < expr.getArguments().size(); ++i) {
|
||||
writer.print(", ");
|
||||
expr.getArguments().get(i).acceptVisitor(this);
|
||||
}
|
||||
writer.print("))");
|
||||
|
||||
temporaryReceiverLevel--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(QualificationExpr expr) {
|
||||
if (expr.getQualified() != null) {
|
||||
writer.print("FIELD(");
|
||||
expr.getQualified().acceptVisitor(this);
|
||||
writer.print(", ").print(names.forClass(expr.getField().getClassName()) + ", "
|
||||
+ names.forMemberField(expr.getField()) + ")");
|
||||
} else {
|
||||
writer.print(names.forStaticField(expr.getField()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(NewExpr expr) {
|
||||
allocObject(expr.getConstructedClass());
|
||||
}
|
||||
|
||||
private void allocObject(String className) {
|
||||
writer.print(names.forMethod(ALLOC_METHOD)).print("(&")
|
||||
.print(names.forClassInstance(ValueType.object(className)))
|
||||
.print(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(NewArrayExpr expr) {
|
||||
writer.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&")
|
||||
.print(names.forClassInstance(ValueType.arrayOf(expr.getType()))).print(", ");
|
||||
expr.getLength().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(NewMultiArrayExpr expr) {
|
||||
writer.print(names.forMethod(ALLOC_MULTI_ARRAY_METHOD)).print("(&")
|
||||
.print(names.forClassInstance(expr.getType())).print(", ");
|
||||
|
||||
writer.print("(int32_t[]) {");
|
||||
expr.getDimensions().get(0).acceptVisitor(this);
|
||||
for (int i = 1; i < expr.getDimensions().size(); ++i) {
|
||||
writer.print(", ");
|
||||
expr.getDimensions().get(i).acceptVisitor(this);
|
||||
}
|
||||
|
||||
writer.print("}, ").print(String.valueOf(expr.getDimensions().size())).print(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InstanceOfExpr expr) {
|
||||
writer.print("instanceof(");
|
||||
expr.getExpr().acceptVisitor(this);
|
||||
writer.print(", ").print(names.forSupertypeFunction(expr.getType())).print(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastExpr expr) {
|
||||
if (expr.getTarget() instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) expr.getTarget()).getClassName();
|
||||
if (context.getCharacteristics().isStructure(className)
|
||||
|| className.equals(Address.class.getName())) {
|
||||
expr.getValue().acceptVisitor(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
writer.print("checkcast(");
|
||||
expr.getValue().acceptVisitor(this);
|
||||
writer.print(", ").print(names.forSupertypeFunction(expr.getTarget())).print(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PrimitiveCastExpr expr) {
|
||||
writer.print("((");
|
||||
switch (expr.getTarget()) {
|
||||
case INT:
|
||||
writer.print("int32_t");
|
||||
break;
|
||||
case LONG:
|
||||
writer.print("int64_t");
|
||||
break;
|
||||
case FLOAT:
|
||||
writer.print("float");
|
||||
break;
|
||||
case DOUBLE:
|
||||
writer.print("double");
|
||||
break;
|
||||
}
|
||||
writer.print(") ");
|
||||
expr.getValue().acceptVisitor(this);
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(AssignmentStatement statement) {
|
||||
if (statement.getLeftValue() != null) {
|
||||
statement.getLeftValue().acceptVisitor(this);
|
||||
writer.print(" = ");
|
||||
}
|
||||
statement.getRightValue().acceptVisitor(this);
|
||||
writer.println(";");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SequentialStatement statement) {
|
||||
visitMany(statement.getSequence());
|
||||
}
|
||||
|
||||
private void visitMany(List<Statement> statements) {
|
||||
for (Statement statement : statements) {
|
||||
statement.acceptVisitor(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ConditionalStatement statement) {
|
||||
while (true) {
|
||||
writer.print("if (");
|
||||
statement.getCondition().acceptVisitor(this);
|
||||
writer.println(") {").indent();
|
||||
|
||||
visitMany(statement.getConsequent());
|
||||
writer.outdent().print("}");
|
||||
|
||||
if (statement.getAlternative().isEmpty()) {
|
||||
writer.println();
|
||||
break;
|
||||
}
|
||||
|
||||
writer.print(" else ");
|
||||
if (statement.getAlternative().size() == 1
|
||||
&& statement.getAlternative().get(0) instanceof ConditionalStatement) {
|
||||
statement = (ConditionalStatement) statement.getAlternative().get(0);
|
||||
} else {
|
||||
writer.println("{").indent();
|
||||
visitMany(statement.getAlternative());
|
||||
writer.outdent().println("}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(SwitchStatement statement) {
|
||||
writer.print("switch (");
|
||||
statement.getValue().acceptVisitor(this);
|
||||
writer.print(") {").println().indent();
|
||||
|
||||
for (SwitchClause clause : statement.getClauses()) {
|
||||
for (int condition : clause.getConditions()) {
|
||||
writer.println("case " + condition + ":");
|
||||
}
|
||||
|
||||
writer.indent();
|
||||
visitMany(clause.getBody());
|
||||
writer.println("break;");
|
||||
writer.outdent();
|
||||
}
|
||||
|
||||
if (!statement.getDefaultClause().isEmpty()) {
|
||||
writer.println("default:").indent();
|
||||
visitMany(statement.getDefaultClause());
|
||||
writer.outdent();
|
||||
}
|
||||
|
||||
writer.outdent().println("}");
|
||||
|
||||
if (statement.getId() != null) {
|
||||
writer.outdent().println("label_" + statement.getId() + ":;").indent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(WhileStatement statement) {
|
||||
writer.print("while (");
|
||||
if (statement.getCondition() != null) {
|
||||
statement.getCondition().acceptVisitor(this);
|
||||
} else {
|
||||
writer.print("1");
|
||||
}
|
||||
writer.println(") {").indent();
|
||||
|
||||
visitMany(statement.getBody());
|
||||
|
||||
if (statement.getId() != null) {
|
||||
writer.outdent().println("cnt_" + statement.getId() + ":;").indent();
|
||||
}
|
||||
writer.outdent().println("}");
|
||||
|
||||
if (statement.getId() != null) {
|
||||
writer.outdent().println("label_" + statement.getId() + ":;").indent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BlockStatement statement) {
|
||||
visitMany(statement.getBody());
|
||||
|
||||
if (statement.getId() != null) {
|
||||
writer.outdent().println("label_" + statement.getId() + ":;").indent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BreakStatement statement) {
|
||||
if (statement.getTarget() == null || statement.getTarget().getId() == null) {
|
||||
writer.println("break;");
|
||||
} else {
|
||||
writer.println("goto label_" + statement.getTarget().getId() + ";");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ContinueStatement statement) {
|
||||
if (statement.getTarget() == null || statement.getTarget().getId() == null) {
|
||||
writer.println("continue;");
|
||||
} else {
|
||||
writer.println("goto cnt_" + statement.getTarget().getId() + ";");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ReturnStatement statement) {
|
||||
writer.print("return");
|
||||
if (statement.getResult() != null) {
|
||||
writer.print(" ");
|
||||
statement.getResult().acceptVisitor(this);
|
||||
}
|
||||
writer.println(";");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ThrowStatement statement) {
|
||||
writer.print(names.forMethod(THROW_EXCEPTION_METHOD)).print("(");
|
||||
statement.getException().acceptVisitor(this);
|
||||
writer.println(");");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InitClassStatement statement) {
|
||||
writer.println(names.forClassInitializer(statement.getClassName()) + "();");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(TryCatchStatement statement) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(GotoPartStatement statement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MonitorEnterStatement statement) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MonitorExitStatement statement) {
|
||||
}
|
||||
|
||||
private IntrinsicContext intrinsicContext = new IntrinsicContext() {
|
||||
@Override
|
||||
public CodeWriter writer() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameProvider names() {
|
||||
return names;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emit(Expr expr) {
|
||||
expr.acceptVisitor(CodeGenerationVisitor.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diagnostics getDiagnotics() {
|
||||
return context.getDiagnostics();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodReference getCallingMethod() {
|
||||
return callingMethod;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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.generate;
|
||||
|
||||
import org.teavm.ast.RegularMethodNode;
|
||||
import org.teavm.ast.VariableNode;
|
||||
import org.teavm.backend.c.analyze.TemporaryVariableEstimator;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class CodeGenerator {
|
||||
private GenerationContext context;
|
||||
private CodeWriter writer;
|
||||
private NameProvider names;
|
||||
|
||||
public CodeGenerator(GenerationContext context, CodeWriter writer) {
|
||||
this.context = context;
|
||||
this.writer = writer;
|
||||
this.names = context.getNames();
|
||||
}
|
||||
|
||||
public void generateMethod(RegularMethodNode methodNode) {
|
||||
generateMethodSignature(methodNode.getReference(),
|
||||
methodNode.getModifiers().contains(ElementModifier.STATIC), true);
|
||||
|
||||
writer.print(" {").indent().println();
|
||||
|
||||
generateLocals(methodNode);
|
||||
|
||||
CodeGenerationVisitor visitor = new CodeGenerationVisitor(context, writer);
|
||||
visitor.setCallingMethod(methodNode.getReference());
|
||||
methodNode.getBody().acceptVisitor(visitor);
|
||||
|
||||
writer.outdent().println("}");
|
||||
}
|
||||
|
||||
public void generateMethodSignature(MethodReference methodRef, boolean isStatic, boolean withNames) {
|
||||
writer.print("static ");
|
||||
writer.printType(methodRef.getReturnType()).print(" ").print(names.forMethod(methodRef)).print("(");
|
||||
|
||||
generateMethodParameters(methodRef.getDescriptor(), isStatic, withNames);
|
||||
|
||||
writer.print(")");
|
||||
}
|
||||
|
||||
public void generateMethodParameters(MethodDescriptor methodRef, boolean isStatic, boolean withNames) {
|
||||
if (methodRef.parameterCount() == 0 && isStatic) {
|
||||
return;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
if (!isStatic) {
|
||||
writer.print("void*");
|
||||
if (withNames) {
|
||||
writer.print(" _this_");
|
||||
}
|
||||
} else {
|
||||
writer.printType(methodRef.parameterType(0));
|
||||
if (withNames) {
|
||||
writer.print(" local_1");
|
||||
}
|
||||
start++;
|
||||
}
|
||||
|
||||
for (int i = start; i < methodRef.parameterCount(); ++i) {
|
||||
writer.print(", ").printType(methodRef.parameterType(i));
|
||||
if (withNames) {
|
||||
writer.print(" ").print("local_").print(String.valueOf(i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateLocals(RegularMethodNode methodNode) {
|
||||
int start = methodNode.getReference().parameterCount() + 1;
|
||||
for (int i = start; i < methodNode.getVariables().size(); ++i) {
|
||||
VariableNode variableNode = methodNode.getVariables().get(i);
|
||||
writer.printType(variableNode.getType()).print(" local_").print(String.valueOf(i)).println(";");
|
||||
}
|
||||
|
||||
TemporaryVariableEstimator temporaryEstimator = new TemporaryVariableEstimator();
|
||||
methodNode.getBody().acceptVisitor(temporaryEstimator);
|
||||
for (int i = 0; i < temporaryEstimator.getMaxReceiverIndex(); ++i) {
|
||||
writer.print("void* recv_").print(String.valueOf(i)).println(";");
|
||||
}
|
||||
}
|
||||
}
|
151
core/src/main/java/org/teavm/backend/c/generate/CodeWriter.java
Normal file
151
core/src/main/java/org/teavm/backend/c/generate/CodeWriter.java
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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.generate;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.util.VariableType;
|
||||
|
||||
public class CodeWriter {
|
||||
private PrintWriter writer;
|
||||
private int indentLevel;
|
||||
private boolean isLineStart;
|
||||
|
||||
public CodeWriter(PrintWriter writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public CodeWriter println() {
|
||||
return println("");
|
||||
}
|
||||
|
||||
public CodeWriter println(String string) {
|
||||
addIndentIfNecessary();
|
||||
writer.print(string);
|
||||
writer.print("\n");
|
||||
isLineStart = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter print(String string) {
|
||||
addIndentIfNecessary();
|
||||
writer.print(string);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter indent() {
|
||||
indentLevel++;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter outdent() {
|
||||
indentLevel--;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addIndentIfNecessary() {
|
||||
if (isLineStart) {
|
||||
for (int i = 0; i < indentLevel; ++i) {
|
||||
writer.print(" ");
|
||||
}
|
||||
isLineStart = false;
|
||||
}
|
||||
}
|
||||
|
||||
public CodeWriter printType(ValueType type) {
|
||||
print(typeAsString(type));
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter printStrictType(ValueType type) {
|
||||
print(strictTypeAsString(type));
|
||||
return this;
|
||||
}
|
||||
|
||||
public static String strictTypeAsString(ValueType type) {
|
||||
if (type instanceof ValueType.Primitive) {
|
||||
switch (((ValueType.Primitive) type).getKind()) {
|
||||
case BOOLEAN:
|
||||
case BYTE:
|
||||
return "int8_t";
|
||||
case SHORT:
|
||||
return "int16_t";
|
||||
case CHARACTER:
|
||||
return "char16_t";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return typeAsString(type);
|
||||
}
|
||||
|
||||
public static String typeAsString(ValueType type) {
|
||||
if (type instanceof ValueType.Primitive) {
|
||||
switch (((ValueType.Primitive) type).getKind()) {
|
||||
case BOOLEAN:
|
||||
case BYTE:
|
||||
case SHORT:
|
||||
case CHARACTER:
|
||||
case INTEGER:
|
||||
return "int32_t";
|
||||
case LONG:
|
||||
return "int64_t";
|
||||
case FLOAT:
|
||||
return "float";
|
||||
case DOUBLE:
|
||||
return "double";
|
||||
}
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
return "JavaArray*";
|
||||
} else if (type == ValueType.VOID) {
|
||||
return "void";
|
||||
}
|
||||
|
||||
return "void*";
|
||||
}
|
||||
|
||||
public CodeWriter printType(VariableType type) {
|
||||
switch (type) {
|
||||
case INT:
|
||||
print("int32_t");
|
||||
break;
|
||||
case LONG:
|
||||
print("int64_t");
|
||||
break;
|
||||
case FLOAT:
|
||||
print("float");
|
||||
break;
|
||||
case DOUBLE:
|
||||
print("double");
|
||||
break;
|
||||
case OBJECT:
|
||||
print("void*");
|
||||
break;
|
||||
case BYTE_ARRAY:
|
||||
case CHAR_ARRAY:
|
||||
case SHORT_ARRAY:
|
||||
case INT_ARRAY:
|
||||
case LONG_ARRAY:
|
||||
case FLOAT_ARRAY:
|
||||
case DOUBLE_ARRAY:
|
||||
case OBJECT_ARRAY:
|
||||
print("JavaArray*");
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.generate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.teavm.backend.c.analyze.Characteristics;
|
||||
import org.teavm.backend.c.intrinsic.Intrinsic;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.classes.VirtualTableProvider;
|
||||
|
||||
public class GenerationContext {
|
||||
private VirtualTableProvider virtualTableProvider;
|
||||
private Characteristics characteristics;
|
||||
private StringPool stringPool;
|
||||
private NameProvider names;
|
||||
private Diagnostics diagnostics;
|
||||
private ClassReaderSource classSource;
|
||||
private List<Intrinsic> intrinsics;
|
||||
private Map<MethodReference, Intrinsic> intrinsicCache = new HashMap<>();
|
||||
|
||||
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
|
||||
StringPool stringPool, NameProvider names, Diagnostics diagnostics, ClassReaderSource classSource,
|
||||
List<Intrinsic> intrinsics) {
|
||||
this.virtualTableProvider = virtualTableProvider;
|
||||
this.characteristics = characteristics;
|
||||
this.stringPool = stringPool;
|
||||
this.names = names;
|
||||
this.diagnostics = diagnostics;
|
||||
this.classSource = classSource;
|
||||
this.intrinsics = new ArrayList<>(intrinsics);
|
||||
}
|
||||
|
||||
public VirtualTableProvider getVirtualTableProvider() {
|
||||
return virtualTableProvider;
|
||||
}
|
||||
|
||||
public Characteristics getCharacteristics() {
|
||||
return characteristics;
|
||||
}
|
||||
|
||||
public StringPool getStringPool() {
|
||||
return stringPool;
|
||||
}
|
||||
|
||||
public NameProvider getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
public Diagnostics getDiagnostics() {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
public ClassReaderSource getClassSource() {
|
||||
return classSource;
|
||||
}
|
||||
|
||||
public Intrinsic getIntrinsic(MethodReference method) {
|
||||
return intrinsicCache.computeIfAbsent(method,
|
||||
m -> intrinsics.stream().filter(i -> i.canHandle(m)).findFirst().orElse(null));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* 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.generate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.interop.Export;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.runtime.RuntimeArray;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.runtime.RuntimeObject;
|
||||
|
||||
public class NameProvider {
|
||||
private ClassReaderSource classSource;
|
||||
|
||||
private Set<String> occupiedTopLevelNames = new HashSet<>();
|
||||
private Map<String, Set<String>> occupiedVtableNames = new HashMap<>();
|
||||
private Map<String, Set<String>> occupiedClassNames = new HashMap<>();
|
||||
|
||||
private Map<MethodReference, String> methodNames = new HashMap<>();
|
||||
private Map<MethodReference, String> virtualMethodNames = new HashMap<>();
|
||||
|
||||
private Map<FieldReference, String> staticFieldNames = new HashMap<>();
|
||||
private Map<FieldReference, String> memberFieldNames = new HashMap<>();
|
||||
|
||||
private Map<String, String> classNames = new HashMap<>();
|
||||
private Map<String, String> classInitializerNames = new HashMap<>();
|
||||
private Map<String, String> classClassNames = new HashMap<>();
|
||||
private Map<ValueType, String> classInstanceNames = new HashMap<>();
|
||||
private Map<ValueType, String> supertypeNames = new HashMap<>();
|
||||
|
||||
public NameProvider(ClassReaderSource classSource) {
|
||||
this.classSource = classSource;
|
||||
|
||||
occupiedTopLevelNames.add("JavaObject");
|
||||
occupiedTopLevelNames.add("JavaArray");
|
||||
occupiedTopLevelNames.add("JavaString");
|
||||
occupiedTopLevelNames.add("JavaClass");
|
||||
|
||||
classNames.put(RuntimeObject.class.getName(), "JavaObject");
|
||||
classNames.put(String.class.getName(), "JavaString");
|
||||
classNames.put(RuntimeClass.class.getName(), "JavaClass");
|
||||
classNames.put(RuntimeArray.class.getName(), "JavaArray");
|
||||
|
||||
memberFieldNames.put(new FieldReference(RuntimeObject.class.getName(), "classReference"), "header");
|
||||
memberFieldNames.put(new FieldReference(RuntimeArray.class.getName(), "size"), "size");
|
||||
|
||||
occupiedClassNames.put(RuntimeObject.class.getName(), new HashSet<>(Arrays.asList("header")));
|
||||
occupiedClassNames.put(RuntimeArray.class.getName(), new HashSet<>(Arrays.asList("length")));
|
||||
}
|
||||
|
||||
public String forMethod(MethodReference method) {
|
||||
return methodNames.computeIfAbsent(method, k -> {
|
||||
String specialName = getSpecialName(k);
|
||||
return specialName == null ? pickUnoccupied(suggestForMethod(k)) : specialName;
|
||||
});
|
||||
}
|
||||
|
||||
public String forVirtualMethod(MethodReference method) {
|
||||
return virtualMethodNames.computeIfAbsent(method, k -> {
|
||||
Set<String> occupied = occupiedVtableNames.computeIfAbsent(k.getClassName(),
|
||||
c -> new HashSet<>(Arrays.asList("parent")));
|
||||
return pickUnoccupied(k.getName(), occupied);
|
||||
});
|
||||
}
|
||||
|
||||
private String getSpecialName(MethodReference methodReference) {
|
||||
MethodReader method = classSource.resolve(methodReference);
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AnnotationReader exportAnnot = method.getAnnotations().get(Export.class.getName());
|
||||
if (exportAnnot != null) {
|
||||
return exportAnnot.getValue("name").getString();
|
||||
}
|
||||
|
||||
AnnotationReader importAnnot = method.getAnnotations().get(Import.class.getName());
|
||||
if (importAnnot != null) {
|
||||
return importAnnot.getValue("name").getString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String forStaticField(FieldReference field) {
|
||||
return staticFieldNames.computeIfAbsent(field, k -> pickUnoccupied(suggestForStaticField(k)));
|
||||
}
|
||||
|
||||
public String forMemberField(FieldReference field) {
|
||||
return memberFieldNames.computeIfAbsent(field, k -> {
|
||||
Set<String> occupied = occupiedClassNames.computeIfAbsent(k.getClassName(),
|
||||
c -> new HashSet<>(Arrays.asList("parent")));
|
||||
return pickUnoccupied(field.getFieldName(), occupied);
|
||||
});
|
||||
}
|
||||
|
||||
public String forClass(String className) {
|
||||
return classNames.computeIfAbsent(className, k -> pickUnoccupied(suggestForClass(k)));
|
||||
}
|
||||
|
||||
public String forClassInitializer(String className) {
|
||||
return classInitializerNames.computeIfAbsent(className, k -> pickUnoccupied("initclass_" + suggestForClass(k)));
|
||||
}
|
||||
|
||||
public String forClassClass(String className) {
|
||||
return classClassNames.computeIfAbsent(className, k -> pickUnoccupied(suggestForClass(k) + "_VT"));
|
||||
}
|
||||
|
||||
public String forClassInstance(ValueType type) {
|
||||
return classInstanceNames.computeIfAbsent(type, k -> pickUnoccupied(suggestForType(k) + "_Cls"));
|
||||
}
|
||||
|
||||
public String forSupertypeFunction(ValueType type) {
|
||||
return supertypeNames.computeIfAbsent(type, k -> pickUnoccupied("supertypeof_" + suggestForType(k)));
|
||||
}
|
||||
|
||||
private String suggestForMethod(MethodReference method) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
suggestForClass(method.getClassName(), sb);
|
||||
sb.append('_');
|
||||
sb.append(sanitize(method.getName()));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String suggestForStaticField(FieldReference field) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
suggestForClass(field.getClassName(), sb);
|
||||
sb.append('_');
|
||||
sb.append(field.getFieldName());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String suggestForClass(String className) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
suggestForClass(className, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void suggestForClass(String className, StringBuilder sb) {
|
||||
int index = 0;
|
||||
while (true) {
|
||||
int next = className.indexOf('.', index);
|
||||
if (next < 0) {
|
||||
if (index > 0) {
|
||||
sb.append('_');
|
||||
sb.append(sanitize(className.substring(index)));
|
||||
} else {
|
||||
sb.append(sanitize(className));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sb.append(sanitize(String.valueOf(className.charAt(index))));
|
||||
index = next + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private String suggestForType(ValueType type) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
suggestForType(type, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void suggestForType(ValueType type, StringBuilder sb) {
|
||||
if (type instanceof ValueType.Object) {
|
||||
suggestForClass(((ValueType.Object) type).getClassName(), sb);
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
sb.append("Arr_");
|
||||
suggestForType(((ValueType.Array) type).getItemType(), sb);
|
||||
} else {
|
||||
sb.append(type.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private String sanitize(String name) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < name.length(); ++i) {
|
||||
char c = name.charAt(i);
|
||||
switch (c) {
|
||||
case '>':
|
||||
case '<':
|
||||
case '$':
|
||||
sb.append('_');
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String pickUnoccupied(String name) {
|
||||
return pickUnoccupied(name, occupiedTopLevelNames);
|
||||
}
|
||||
|
||||
private String pickUnoccupied(String name, Set<String> occupied) {
|
||||
String result = name;
|
||||
int index = 0;
|
||||
while (!occupied.add(result)) {
|
||||
result = name + "_" + index++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.generate;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class StringPool {
|
||||
private final ObjectIntMap<String> stringIndexes = new ObjectIntHashMap<>();
|
||||
private final List<String> strings = new ArrayList<>();
|
||||
private final List<String> readonlyStrings = Collections.unmodifiableList(strings);
|
||||
|
||||
public int getStringIndex(String string) {
|
||||
int index = stringIndexes.getOrDefault(string, -1);
|
||||
if (index < 0) {
|
||||
index = strings.size();
|
||||
stringIndexes.put(string, index);
|
||||
strings.add(string);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public List<String> getStrings() {
|
||||
return readonlyStrings;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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.generate;
|
||||
|
||||
import java.util.List;
|
||||
import org.teavm.model.FieldReference;
|
||||
|
||||
public class StringPoolGenerator {
|
||||
private CodeWriter writer;
|
||||
private NameProvider names;
|
||||
|
||||
public StringPoolGenerator(CodeWriter writer, NameProvider names) {
|
||||
this.writer = writer;
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
public void generate(List<? extends String> strings) {
|
||||
generateStringArrays(strings);
|
||||
generateStringObjects(strings);
|
||||
}
|
||||
|
||||
private void generateStringArrays(List<? extends String> strings) {
|
||||
for (int i = 0; i < strings.size(); ++i) {
|
||||
String s = strings.get(i);
|
||||
writer.print("static struct { JavaArray hdr; char16_t data[" + s.length() + 1 + "]; } str_array_" + i)
|
||||
.println(" = {").indent();
|
||||
writer.println(".hdr = { .parent = {}, .size = " + s.length() + "},");
|
||||
writer.print(".data = ");
|
||||
generateStringLiteral(s);
|
||||
writer.println();
|
||||
|
||||
writer.outdent().println("};");
|
||||
}
|
||||
}
|
||||
|
||||
private void generateStringObjects(List<? extends String> strings) {
|
||||
String charactersName = names.forMemberField(new FieldReference(String.class.getName(), "characters"));
|
||||
String hashCodeName = names.forMemberField(new FieldReference(String.class.getName(), "hashCode"));
|
||||
|
||||
writer.println("static JavaString stringPool[" + strings.size() + "] = {").indent();
|
||||
for (int i = 0; i < strings.size(); ++i) {
|
||||
writer.println("{").indent();
|
||||
writer.println(".parent = {},");
|
||||
writer.println("." + charactersName + " = (JavaArray*) &str_array_" + i + ",");
|
||||
writer.println("." + hashCodeName + " = INT32_C(" + strings.get(i).hashCode() + ")");
|
||||
writer.outdent().print("}");
|
||||
|
||||
if (i < strings.size() - 1) {
|
||||
writer.print(",");
|
||||
}
|
||||
writer.println();
|
||||
}
|
||||
writer.outdent().println("};");
|
||||
}
|
||||
|
||||
private void generateStringLiteral(String string) {
|
||||
writer.print("u\"");
|
||||
|
||||
for (int j = 0; j < string.length(); ++j) {
|
||||
char c = string.charAt(j);
|
||||
switch (c) {
|
||||
case '\r':
|
||||
writer.print("\\r");
|
||||
break;
|
||||
case '\n':
|
||||
writer.print("\\n");
|
||||
break;
|
||||
case '\t':
|
||||
writer.print("\\t");
|
||||
break;
|
||||
default:
|
||||
if (c < 32 || c > 127) {
|
||||
writer.print("\\u"
|
||||
+ Character.forDigit(c >> 12, 16)
|
||||
+ Character.forDigit((c >> 8) & 15, 16)
|
||||
+ Character.forDigit((c >> 4) & 15, 16)
|
||||
+ Character.forDigit(c & 15, 16));
|
||||
} else {
|
||||
writer.print(String.valueOf(c));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
writer.print("\"");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.interop.Address;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class AddressIntrinsic implements Intrinsic {
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(Address.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "fromInt":
|
||||
case "fromLong":
|
||||
case "toInt":
|
||||
case "toLong":
|
||||
case "toStructure":
|
||||
case "ofObject":
|
||||
|
||||
case "getByte":
|
||||
case "getShort":
|
||||
case "getChar":
|
||||
case "getInt":
|
||||
case "getLong":
|
||||
case "getFloat":
|
||||
case "getDouble":
|
||||
case "getAddress":
|
||||
|
||||
case "putByte":
|
||||
case "putShort":
|
||||
case "putChar":
|
||||
case "putInt":
|
||||
case "putLong":
|
||||
case "putFloat":
|
||||
case "putDouble":
|
||||
case "putAddress":
|
||||
|
||||
case "add":
|
||||
case "isLessThan":
|
||||
case "align":
|
||||
case "sizeOf":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "fromInt":
|
||||
case "fromLong":
|
||||
context.writer().print("((void*) (intptr_t) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "toInt":
|
||||
context.writer().print("((int32_t) (intptr_t) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "toLong":
|
||||
context.writer().print("((int64_t) (intptr_t) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "toStructure":
|
||||
case "ofObject":
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
break;
|
||||
|
||||
case "getByte":
|
||||
context.writer().print("((int32_t) *(int8_t*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "getShort":
|
||||
context.writer().print("((int32_t) *(int16_t*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "getChar":
|
||||
context.writer().print("((int32_t) *(uchar16_t*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "getInt":
|
||||
getValue(context, invocation, "int32_t");
|
||||
break;
|
||||
case "getLong":
|
||||
getValue(context, invocation, "int64_t");
|
||||
break;
|
||||
case "getFloat":
|
||||
getValue(context, invocation, "float");
|
||||
break;
|
||||
case "getDouble":
|
||||
getValue(context, invocation, "double");
|
||||
break;
|
||||
case "getAddress":
|
||||
getValue(context, invocation, "void*");
|
||||
break;
|
||||
|
||||
case "putByte":
|
||||
context.writer().print("(*(int8_t*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(" = (int8_t) ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "putShort":
|
||||
context.writer().print("(*(int16_t*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(" = (int16_t) ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "putChar":
|
||||
context.writer().print("(*(uchar16_t*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(" = (uchar16_t) ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "putInt":
|
||||
putValue(context, invocation, "int32_t");
|
||||
break;
|
||||
case "putLong":
|
||||
putValue(context, invocation, "int64_t");
|
||||
break;
|
||||
case "putFloat":
|
||||
putValue(context, invocation, "float");
|
||||
break;
|
||||
case "putDouble":
|
||||
putValue(context, invocation, "double");
|
||||
break;
|
||||
case "putAddress":
|
||||
putValue(context, invocation, "void*");
|
||||
break;
|
||||
|
||||
case "add":
|
||||
if (invocation.getArguments().size() == 2) {
|
||||
context.writer().print("ADDRESS_ADD(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(", ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
} else {
|
||||
context.writer().print("ADDRESS_ADD(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(", ");
|
||||
String className = StructureIntrinsic.getClassLiteral(context, invocation,
|
||||
invocation.getArguments().get(1));
|
||||
context.emit(invocation.getArguments().get(2));
|
||||
context.writer().print(" * sizeof(").print(context.names().forClass(className)).print(")");
|
||||
context.writer().print(")");
|
||||
}
|
||||
break;
|
||||
case "isLessThan":
|
||||
context.writer().print("((uintptr_t) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(" < (uintptr_t) ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "align":
|
||||
context.writer().print("ALIGN(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(", ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "sizeOf":
|
||||
context.writer().print("sizeof(void*)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void getValue(IntrinsicContext context, InvocationExpr invocation, String type) {
|
||||
context.writer().print("(*(" + type + "*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
}
|
||||
|
||||
private void putValue(IntrinsicContext context, InvocationExpr invocation, String type) {
|
||||
context.writer().print("(*(" + type + "*) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(" = ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
|
||||
public class AllocatorIntrinsic implements Intrinsic {
|
||||
private static final FieldReference FLAGS_FIELD = new FieldReference(RuntimeClass.class.getName(), "flags");
|
||||
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(Allocator.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "fillZero":
|
||||
case "moveMemoryBlock":
|
||||
case "isInitialized":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "fillZero":
|
||||
context.writer().print("memset(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(", 0, ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "moveMemoryBlock":
|
||||
context.writer().print("memmove(");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(", ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(", ");
|
||||
context.emit(invocation.getArguments().get(2));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
case "isInitialized":
|
||||
context.writer().print("(((JavaClass *) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")->").print(context.names().forMemberField(FLAGS_FIELD))
|
||||
.print(" & INT32_C(" + RuntimeClass.INITIALIZED + "))");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.runtime.ExceptionHandling;
|
||||
|
||||
public class ExceptionHandlingIntrinsic implements Intrinsic {
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(ExceptionHandling.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "findCallSiteById":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "findCallSiteById":
|
||||
context.writer().print("(callSites + ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.c.analyze.Characteristics;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class FunctionIntrinsic implements Intrinsic {
|
||||
private Characteristics characteristics;
|
||||
|
||||
public FunctionIntrinsic(Characteristics characteristics) {
|
||||
this.characteristics = characteristics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
return characteristics.isFunction(method.getClassName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
MethodReference method = invocation.getMethod();
|
||||
context.writer().print("(((").printType(method.getReturnType()).print(" (*)(");
|
||||
if (method.parameterCount() > 0) {
|
||||
context.writer().printType(method.parameterType(0));
|
||||
for (int i = 1; i < method.parameterCount(); ++i) {
|
||||
context.writer().print(", ").printType(method.parameterType(i));
|
||||
}
|
||||
}
|
||||
context.writer().print(")) ");
|
||||
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")(");
|
||||
if (invocation.getArguments().size() > 1) {
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
for (int i = 2; i < invocation.getArguments().size(); ++i) {
|
||||
context.writer().print(", ");
|
||||
context.emit(invocation.getArguments().get(i));
|
||||
}
|
||||
}
|
||||
context.writer().print("))");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.runtime.GC;
|
||||
|
||||
public class GCIntrinsic implements Intrinsic {
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(GC.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "gcStorageAddress":
|
||||
case "gcStorageSize":
|
||||
case "heapAddress":
|
||||
case "regionsAddress":
|
||||
case "regionMaxCount":
|
||||
case "availableBytes":
|
||||
case "regionSize":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
context.writer().print("gc_").print(invocation.getMethod().getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public interface Intrinsic {
|
||||
boolean canHandle(MethodReference method);
|
||||
|
||||
void apply(IntrinsicContext context, InvocationExpr invocation);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.Expr;
|
||||
import org.teavm.backend.c.generate.CodeWriter;
|
||||
import org.teavm.backend.c.generate.NameProvider;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public interface IntrinsicContext {
|
||||
CodeWriter writer();
|
||||
|
||||
NameProvider names();
|
||||
|
||||
void emit(Expr expr);
|
||||
|
||||
Diagnostics getDiagnotics();
|
||||
|
||||
MethodReference getCallingMethod();
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.runtime.Mutator;
|
||||
|
||||
public class MutatorIntrinsic implements Intrinsic {
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(Mutator.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "getStaticGCRoots":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "getStaticGCRoots":
|
||||
context.writer().print("gc_staticRoots");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class PlatformClassIntrinsic implements Intrinsic {
|
||||
private static final String PLATFORM_CLASS = "org.teavm.platform.PlatformClass";
|
||||
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(PLATFORM_CLASS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "getJavaClass":
|
||||
case "getMetadata":
|
||||
case "setJavaClass":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "getJavaClass":
|
||||
case "getMetadata":
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
break;
|
||||
case "setJavaClass":
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.backend.c.intrinsic;
|
||||
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
|
||||
public class PlatformClassMetadataIntrinsic implements Intrinsic {
|
||||
private static final String PLATFORM_CLASS_METADATA = "org.teavm.platform.PlatformClassMetadata";
|
||||
private static final FieldReference ARRAY_TYPE_FIELD = new FieldReference(
|
||||
RuntimeClass.class.getName(), "arrayType");
|
||||
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(PLATFORM_CLASS_METADATA)) {
|
||||
return false;
|
||||
}
|
||||
return method.getName().equals("getArrayItem");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "getArrayItem":
|
||||
context.writer().print("FIELD(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(",");
|
||||
context.writer().print(context.names().forClass(ARRAY_TYPE_FIELD.getClassName())).print(", ");
|
||||
context.writer().print(context.names().forMemberField(ARRAY_TYPE_FIELD));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.backend.c.intrinsic;
|
||||
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class PlatformIntrinsic implements Intrinsic {
|
||||
private static final String PLATFORM = "org.teavm.platform.Platform";
|
||||
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(PLATFORM)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "getPlatformObject":
|
||||
case "asJavaClass":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "getPlatformObject":
|
||||
case "asJavaClass":
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
break;
|
||||
case "getName":
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class PlatformObjectIntrinsic implements Intrinsic {
|
||||
private static final String PLATFORM_OBJECT = "org.teavm.platform.PlatformObject";
|
||||
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(PLATFORM_OBJECT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return method.getName().equals("getPlatformClass");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "getPlatformClass":
|
||||
context.writer().print("CLASS_OF(");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.runtime.ShadowStack;
|
||||
|
||||
public class ShadowStackIntrinsic implements Intrinsic {
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!method.getClassName().equals(ShadowStack.class.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "allocStack":
|
||||
case "releaseStack":
|
||||
case "registerGCRoot":
|
||||
case "removeGCRoot":
|
||||
case "registerCallSite":
|
||||
case "getExceptionHandlerId":
|
||||
case "setExceptionHandlerId":
|
||||
case "getStackTop":
|
||||
case "getNextStackFrame":
|
||||
case "getStackRootCount":
|
||||
case "getStackRootPointer":
|
||||
case "getCallSiteId":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "allocStack":
|
||||
context.writer().print("ALLOC_STACK");
|
||||
break;
|
||||
case "releaseStack":
|
||||
context.writer().print("RELEASE_STACK");
|
||||
return;
|
||||
case "registerGCRoot":
|
||||
context.writer().print("GC_ROOT");
|
||||
break;
|
||||
case "removeGCRoot":
|
||||
context.writer().print("GC_ROOT_RELEASE");
|
||||
break;
|
||||
case "registerCallSite":
|
||||
context.writer().print("CALL_SITE");
|
||||
break;
|
||||
case "getExceptionHandlerId":
|
||||
context.writer().print("EXCEPTION_HANDLER");
|
||||
return;
|
||||
case "setExceptionHandlerId":
|
||||
context.writer().print("SET_EXCEPTION_HANDLER");
|
||||
break;
|
||||
case "getStackTop":
|
||||
context.writer().print("stackTop");
|
||||
return;
|
||||
case "getNextStackFrame":
|
||||
context.writer().print("((void**) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")[0]");
|
||||
return;
|
||||
case "getStackRootCount":
|
||||
context.writer().print("((int32_t) (intptr_t) ((void**) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")[2])");
|
||||
return;
|
||||
case "getStackRootPointer":
|
||||
context.writer().print("&((void**) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")[3]");
|
||||
return;
|
||||
case "getCallSiteId":
|
||||
context.writer().print("((int32_t) (intptr_t) ((void**) ");
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
context.writer().print(")[1])");
|
||||
return;
|
||||
}
|
||||
|
||||
context.writer().print("(");
|
||||
for (int i = 0; i < invocation.getArguments().size(); ++i) {
|
||||
if (i > 0) {
|
||||
context.writer().print(", ");
|
||||
}
|
||||
context.emit(invocation.getArguments().get(i));
|
||||
}
|
||||
context.writer().print(")");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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 org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.Expr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.c.analyze.Characteristics;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public class StructureIntrinsic implements Intrinsic {
|
||||
private Characteristics characteristics;
|
||||
|
||||
public StructureIntrinsic(Characteristics characteristics) {
|
||||
this.characteristics = characteristics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(MethodReference method) {
|
||||
if (!characteristics.isStructure(method.getClassName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (method.getName()) {
|
||||
case "cast":
|
||||
case "toAddress":
|
||||
return method.parameterCount() == 0;
|
||||
case "sizeOf":
|
||||
return method.parameterCount() == 1
|
||||
&& method.parameterType(0).equals(ValueType.parse(Class.class));
|
||||
case "add":
|
||||
return method.parameterCount() == 3
|
||||
&& method.parameterType(0).equals(ValueType.parse(Class.class))
|
||||
&& method.parameterType(1).equals(ValueType.parse(Structure.class))
|
||||
&& method.parameterType(2).equals(ValueType.INTEGER);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||
switch (invocation.getMethod().getName()) {
|
||||
case "cast":
|
||||
case "toAddress":
|
||||
context.emit(invocation.getArguments().get(0));
|
||||
break;
|
||||
case "sizeOf": {
|
||||
String className = 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));
|
||||
if (className != null) {
|
||||
context.writer().print("STRUCTURE_ADD(").print(context.names().forClass(className)).print(", ");
|
||||
context.emit(invocation.getArguments().get(1));
|
||||
context.writer().print(", ");
|
||||
context.emit(invocation.getArguments().get(2));
|
||||
context.writer().print(")");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 null;
|
||||
}
|
||||
}
|
|
@ -50,6 +50,7 @@ import org.teavm.dependency.DependencyAnalyzer;
|
|||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.interop.PlatformMarker;
|
||||
import org.teavm.interop.PlatformMarkers;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassHolder;
|
||||
|
@ -448,4 +449,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
}
|
||||
return new SourceLocation(location.getFileName(), location.getLine());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlatformTags() {
|
||||
return new String[] { PlatformMarkers.JAVASCRIPT };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -192,6 +192,7 @@ public final class Example {
|
|||
throwsException();
|
||||
} catch (IllegalStateException e) {
|
||||
System.out.println("Caught 1: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
|
|
|
@ -27,13 +27,13 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import org.teavm.ast.Mangling;
|
||||
import org.teavm.ast.decompilation.Decompiler;
|
||||
import org.teavm.backend.wasm.binary.BinaryWriter;
|
||||
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
||||
import org.teavm.backend.wasm.generate.WasmDependencyListener;
|
||||
import org.teavm.backend.wasm.generate.WasmGenerationContext;
|
||||
import org.teavm.backend.wasm.generate.WasmGenerator;
|
||||
import org.teavm.backend.wasm.generate.WasmMangling;
|
||||
import org.teavm.backend.wasm.generate.WasmStringPool;
|
||||
import org.teavm.backend.wasm.intrinsics.AddressIntrinsic;
|
||||
import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic;
|
||||
|
@ -71,7 +71,6 @@ import org.teavm.backend.wasm.model.expression.WasmReturn;
|
|||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
|
||||
import org.teavm.backend.wasm.optimization.UnusedFunctionElimination;
|
||||
import org.teavm.backend.wasm.patches.ClassPatch;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryRenderer;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
||||
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
||||
|
@ -86,6 +85,7 @@ import org.teavm.dependency.DependencyListener;
|
|||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.PlatformMarkers;
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.BasicBlock;
|
||||
|
@ -114,11 +114,11 @@ import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
|||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
||||
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
|
||||
import org.teavm.model.transformation.ClassPatch;
|
||||
import org.teavm.runtime.Allocator;
|
||||
import org.teavm.runtime.ExceptionHandling;
|
||||
import org.teavm.runtime.RuntimeArray;
|
||||
import org.teavm.runtime.RuntimeClass;
|
||||
import org.teavm.runtime.RuntimeJavaObject;
|
||||
import org.teavm.runtime.RuntimeObject;
|
||||
import org.teavm.vm.BuildTarget;
|
||||
import org.teavm.vm.TeaVMEntryPoint;
|
||||
|
@ -137,6 +137,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
private ShadowStackTransformer shadowStackTransformer;
|
||||
private WasmBinaryVersion version = WasmBinaryVersion.V_0x1;
|
||||
private List<WasmIntrinsicFactory> additionalIntrinsics = new ArrayList<>();
|
||||
private int minHeapSize;
|
||||
|
||||
@Override
|
||||
public void setController(TeaVMTargetController controller) {
|
||||
|
@ -209,6 +210,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
this.version = version;
|
||||
}
|
||||
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||
for (Class<?> type : Arrays.asList(int.class, long.class, float.class, double.class)) {
|
||||
|
@ -260,10 +265,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
|
||||
ClassDependency runtimeClassDep = dependencyAnalyzer.linkClass(RuntimeClass.class.getName(), null);
|
||||
ClassDependency runtimeObjectDep = dependencyAnalyzer.linkClass(RuntimeObject.class.getName(), null);
|
||||
ClassDependency runtimeJavaObjectDep = dependencyAnalyzer.linkClass(RuntimeJavaObject.class.getName(), null);
|
||||
ClassDependency runtimeArrayDep = dependencyAnalyzer.linkClass(RuntimeArray.class.getName(), null);
|
||||
for (ClassDependency classDep : Arrays.asList(runtimeClassDep, runtimeObjectDep, runtimeJavaObjectDep,
|
||||
runtimeArrayDep)) {
|
||||
for (ClassDependency classDep : Arrays.asList(runtimeClassDep, runtimeObjectDep, runtimeArrayDep)) {
|
||||
for (FieldReader field : classDep.getClassReader().getFields()) {
|
||||
dependencyAnalyzer.linkField(field.getReference(), null);
|
||||
}
|
||||
|
@ -288,7 +291,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
TagRegistry tagRegistry = new TagRegistry(classes);
|
||||
BinaryWriter binaryWriter = new BinaryWriter(256);
|
||||
WasmClassGenerator classGenerator = new WasmClassGenerator(
|
||||
classes, vtableProvider, tagRegistry, binaryWriter);
|
||||
controller.getUnprocessedClassSource(), vtableProvider, tagRegistry, binaryWriter);
|
||||
|
||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
||||
new HashSet<>(), false);
|
||||
|
@ -323,7 +326,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
|
||||
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator, binaryWriter);
|
||||
|
||||
module.setMemorySize(128);
|
||||
int pageSize = 1 << 16;
|
||||
int pages = (minHeapSize + pageSize - 1) / pageSize;
|
||||
module.setMemorySize(pages);
|
||||
generateMethods(classes, context, generator, module);
|
||||
exceptionHandlingIntrinsic.postProcess(shadowStackTransformer.getCallSites());
|
||||
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
|
||||
|
@ -350,13 +355,13 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
if (clinit == null) {
|
||||
continue;
|
||||
}
|
||||
initFunction.getBody().add(new WasmCall(WasmMangling.mangleInitializer(className)));
|
||||
initFunction.getBody().add(new WasmCall(Mangling.mangleInitializer(className)));
|
||||
}
|
||||
module.add(initFunction);
|
||||
module.setStartFunction(initFunction);
|
||||
|
||||
for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) {
|
||||
String mangledName = WasmMangling.mangleMethod(entryPoint.getReference());
|
||||
String mangledName = Mangling.mangleMethod(entryPoint.getReference());
|
||||
WasmFunction function = module.getFunctions().get(mangledName);
|
||||
if (function != null) {
|
||||
function.setExportName(entryPoint.getPublicName());
|
||||
|
@ -511,7 +516,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
private void generateIsSupertypeFunctions(TagRegistry tagRegistry, WasmModule module,
|
||||
WasmClassGenerator classGenerator) {
|
||||
for (ValueType type : classGenerator.getRegisteredClasses()) {
|
||||
WasmFunction function = new WasmFunction(WasmMangling.mangleIsSupertype(type));
|
||||
WasmFunction function = new WasmFunction(Mangling.mangleIsSupertype(type));
|
||||
function.getParameters().add(WasmType.INT32);
|
||||
function.setResult(WasmType.INT32);
|
||||
module.add(function);
|
||||
|
@ -603,7 +608,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
itemTest.setType(WasmType.INT32);
|
||||
itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0));
|
||||
|
||||
WasmCall delegateToItem = new WasmCall(WasmMangling.mangleIsSupertype(itemType));
|
||||
WasmCall delegateToItem = new WasmCall(Mangling.mangleIsSupertype(itemType));
|
||||
delegateToItem.getArguments().add(new WasmGetLocal(subtypeVar));
|
||||
itemTest.getElseBlock().getBody().add(delegateToItem);
|
||||
|
||||
|
@ -611,9 +616,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
}
|
||||
|
||||
private void generateStub(WasmModule module, MethodHolder method, MethodHolder implementor) {
|
||||
WasmFunction function = module.getFunctions().get(WasmMangling.mangleMethod(method.getReference()));
|
||||
WasmFunction function = module.getFunctions().get(Mangling.mangleMethod(method.getReference()));
|
||||
|
||||
WasmCall call = new WasmCall(WasmMangling.mangleMethod(implementor.getReference()));
|
||||
WasmCall call = new WasmCall(Mangling.mangleMethod(implementor.getReference()));
|
||||
for (WasmType param : function.getParameters()) {
|
||||
WasmLocal local = new WasmLocal(param);
|
||||
function.add(local);
|
||||
|
@ -647,7 +652,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
continue;
|
||||
}
|
||||
|
||||
WasmFunction initFunction = new WasmFunction(WasmMangling.mangleInitializer(className));
|
||||
WasmFunction initFunction = new WasmFunction(Mangling.mangleInitializer(className));
|
||||
module.add(initFunction);
|
||||
|
||||
WasmBlock block = new WasmBlock(false);
|
||||
|
@ -667,7 +672,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
WasmInt32Subtype.INT32));
|
||||
|
||||
if (method != null) {
|
||||
block.getBody().add(new WasmCall(WasmMangling.mangleMethod(method.getReference())));
|
||||
block.getBody().add(new WasmCall(Mangling.mangleMethod(method.getReference())));
|
||||
}
|
||||
|
||||
if (controller.wasCancelled()) {
|
||||
|
@ -732,4 +737,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
|
||||
return new VirtualTableProvider(classes, virtualMethods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlatformTags() {
|
||||
return new String[] { PlatformMarkers.WEBASSEMBLY, PlatformMarkers.LOW_LEVEL };
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.ast.Mangling;
|
||||
import org.teavm.backend.wasm.binary.BinaryWriter;
|
||||
import org.teavm.backend.wasm.binary.DataArray;
|
||||
import org.teavm.backend.wasm.binary.DataPrimitives;
|
||||
|
@ -168,7 +169,7 @@ public class WasmClassGenerator {
|
|||
binaryData.data.setAddress(CLASS_ITEM_TYPE, itemBinaryData.start);
|
||||
binaryData.data.setInt(CLASS_IS_INSTANCE, functionTable.size());
|
||||
binaryData.data.setInt(CLASS_CANARY, RuntimeClass.computeCanary(4, 0));
|
||||
functionTable.add(WasmMangling.mangleIsSupertype(type));
|
||||
functionTable.add(Mangling.mangleIsSupertype(type));
|
||||
binaryData.start = binaryWriter.append(vtableSize > 0 ? wrapper : binaryData.data);
|
||||
|
||||
itemBinaryData.data.setAddress(CLASS_ARRAY_TYPE, binaryData.start);
|
||||
|
@ -180,7 +181,7 @@ public class WasmClassGenerator {
|
|||
value.setInt(CLASS_SIZE, size);
|
||||
value.setInt(CLASS_FLAGS, RuntimeClass.PRIMITIVE);
|
||||
value.setInt(CLASS_IS_INSTANCE, functionTable.size());
|
||||
functionTable.add(WasmMangling.mangleIsSupertype(type));
|
||||
functionTable.add(Mangling.mangleIsSupertype(type));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -217,7 +218,7 @@ public class WasmClassGenerator {
|
|||
header.setInt(CLASS_CANARY, RuntimeClass.computeCanary(occupiedSize, tag));
|
||||
header.setAddress(CLASS_NAME, stringPool.getStringPointer(name));
|
||||
header.setInt(CLASS_IS_INSTANCE, functionTable.size());
|
||||
functionTable.add(WasmMangling.mangleIsSupertype(ValueType.object(name)));
|
||||
functionTable.add(Mangling.mangleIsSupertype(ValueType.object(name)));
|
||||
header.setAddress(CLASS_PARENT, parentPtr);
|
||||
|
||||
if (vtable != null) {
|
||||
|
@ -322,7 +323,7 @@ public class WasmClassGenerator {
|
|||
} else {
|
||||
methodIndex = functions.computeIfAbsent(vtableEntry.getImplementor(), implementor -> {
|
||||
int result = functionTable.size();
|
||||
functionTable.add(WasmMangling.mangleMethod(implementor));
|
||||
functionTable.add(Mangling.mangleMethod(implementor));
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.teavm.ast.InitClassStatement;
|
|||
import org.teavm.ast.InstanceOfExpr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.ast.InvocationType;
|
||||
import org.teavm.ast.Mangling;
|
||||
import org.teavm.ast.MonitorEnterStatement;
|
||||
import org.teavm.ast.MonitorExitStatement;
|
||||
import org.teavm.ast.NewArrayExpr;
|
||||
|
@ -184,7 +185,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
default:
|
||||
Class<?> type = convertType(expr.getType());
|
||||
MethodReference method = new MethodReference(WasmRuntime.class, "remainder", type, type, type);
|
||||
WasmCall call = new WasmCall(WasmMangling.mangleMethod(method), false);
|
||||
WasmCall call = new WasmCall(Mangling.mangleMethod(method), false);
|
||||
|
||||
accept(expr.getFirstOperand());
|
||||
call.getArguments().add(result);
|
||||
|
@ -238,7 +239,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
case COMPARE: {
|
||||
Class<?> type = convertType(expr.getType());
|
||||
MethodReference method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class);
|
||||
WasmCall call = new WasmCall(WasmMangling.mangleMethod(method), false);
|
||||
WasmCall call = new WasmCall(Mangling.mangleMethod(method), false);
|
||||
|
||||
accept(expr.getFirstOperand());
|
||||
call.getArguments().add(result);
|
||||
|
@ -911,7 +912,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
}
|
||||
|
||||
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
|
||||
String methodName = WasmMangling.mangleMethod(expr.getMethod());
|
||||
String methodName = Mangling.mangleMethod(expr.getMethod());
|
||||
|
||||
WasmCall call = new WasmCall(methodName);
|
||||
if (context.getImportedMethod(expr.getMethod()) != null) {
|
||||
|
@ -930,7 +931,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(),
|
||||
expr.getLocation())));
|
||||
|
||||
String methodName = WasmMangling.mangleMethod(expr.getMethod());
|
||||
String methodName = Mangling.mangleMethod(expr.getMethod());
|
||||
WasmCall call = new WasmCall(methodName);
|
||||
call.getArguments().add(new WasmGetLocal(tmp));
|
||||
for (Expr argument : expr.getArguments()) {
|
||||
|
@ -1200,7 +1201,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
|
||||
private WasmExpression allocateObject(String className, TextLocation location) {
|
||||
int tag = classGenerator.getClassPointer(ValueType.object(className));
|
||||
String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocate",
|
||||
String allocName = Mangling.mangleMethod(new MethodReference(Allocator.class, "allocate",
|
||||
RuntimeClass.class, Address.class));
|
||||
WasmCall call = new WasmCall(allocName);
|
||||
call.getArguments().add(new WasmInt32Constant(tag));
|
||||
|
@ -1213,7 +1214,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
ValueType type = expr.getType();
|
||||
|
||||
int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type));
|
||||
String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocateArray",
|
||||
String allocName = Mangling.mangleMethod(new MethodReference(Allocator.class, "allocateArray",
|
||||
RuntimeClass.class, int.class, Address.class));
|
||||
WasmCall call = new WasmCall(allocName);
|
||||
call.getArguments().add(new WasmInt32Constant(classPointer));
|
||||
|
@ -1243,7 +1244,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
}
|
||||
|
||||
int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type));
|
||||
String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocateMultiArray",
|
||||
String allocName = Mangling.mangleMethod(new MethodReference(Allocator.class, "allocateMultiArray",
|
||||
RuntimeClass.class, Address.class, int.class, RuntimeArray.class));
|
||||
WasmCall call = new WasmCall(allocName);
|
||||
call.getArguments().add(new WasmInt32Constant(classPointer));
|
||||
|
@ -1344,7 +1345,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
|||
@Override
|
||||
public void visit(InitClassStatement statement) {
|
||||
if (classGenerator.hasClinit(statement.getClassName())) {
|
||||
result = new WasmCall(WasmMangling.mangleInitializer(statement.getClassName()));
|
||||
result = new WasmCall(Mangling.mangleInitializer(statement.getClassName()));
|
||||
result.setLocation(statement.getLocation());
|
||||
} else {
|
||||
result = null;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.backend.wasm.generate;
|
||||
|
||||
import org.teavm.ast.Mangling;
|
||||
import org.teavm.ast.RegularMethodNode;
|
||||
import org.teavm.ast.VariableNode;
|
||||
import org.teavm.ast.decompilation.Decompiler;
|
||||
|
@ -51,7 +52,7 @@ public class WasmGenerator {
|
|||
public WasmFunction generateDefinition(MethodReference methodReference) {
|
||||
ClassHolder cls = classSource.get(methodReference.getClassName());
|
||||
MethodHolder method = cls.getMethod(methodReference.getDescriptor());
|
||||
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(method.getReference()));
|
||||
WasmFunction function = new WasmFunction(Mangling.mangleMethod(method.getReference()));
|
||||
|
||||
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||
function.getParameters().add(WasmType.INT32);
|
||||
|
@ -71,7 +72,7 @@ public class WasmGenerator {
|
|||
MethodHolder method = cls.getMethod(methodReference.getDescriptor());
|
||||
|
||||
RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod);
|
||||
WasmFunction function = context.getFunction(WasmMangling.mangleMethod(methodReference));
|
||||
WasmFunction function = context.getFunction(Mangling.mangleMethod(methodReference));
|
||||
int firstVariable = method.hasModifier(ElementModifier.STATIC) ? 1 : 0;
|
||||
for (int i = firstVariable; i < methodAst.getVariables().size(); ++i) {
|
||||
VariableNode variable = methodAst.getVariables().get(i);
|
||||
|
@ -98,7 +99,7 @@ public class WasmGenerator {
|
|||
}
|
||||
|
||||
public WasmFunction generateNative(MethodReference methodReference) {
|
||||
WasmFunction function = context.getFunction(WasmMangling.mangleMethod(methodReference));
|
||||
WasmFunction function = context.getFunction(Mangling.mangleMethod(methodReference));
|
||||
|
||||
WasmGenerationContext.ImportedMethod importedMethod = context.getImportedMethod(methodReference);
|
||||
if (importedMethod != null) {
|
||||
|
|
|
@ -18,9 +18,9 @@ package org.teavm.backend.wasm.intrinsics;
|
|||
import java.util.stream.Collectors;
|
||||
import org.teavm.ast.ConstantExpr;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.ast.Mangling;
|
||||
import org.teavm.backend.wasm.WasmRuntime;
|
||||
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
||||
import org.teavm.backend.wasm.generate.WasmMangling;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmConversion;
|
||||
|
@ -154,7 +154,7 @@ public class AddressIntrinsic implements WasmIntrinsic {
|
|||
case "align": {
|
||||
MethodReference delegate = new MethodReference(WasmRuntime.class.getName(),
|
||||
invocation.getMethod().getDescriptor());
|
||||
WasmCall call = new WasmCall(WasmMangling.mangleMethod(delegate));
|
||||
WasmCall call = new WasmCall(Mangling.mangleMethod(delegate));
|
||||
call.getArguments().addAll(invocation.getArguments().stream()
|
||||
.map(arg -> manager.generate(arg))
|
||||
.collect(Collectors.toList()));
|
||||
|
|
|
@ -17,9 +17,9 @@ package org.teavm.backend.wasm.intrinsics;
|
|||
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.ast.Mangling;
|
||||
import org.teavm.backend.wasm.WasmRuntime;
|
||||
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
||||
import org.teavm.backend.wasm.generate.WasmMangling;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||
|
@ -63,7 +63,7 @@ public class AllocatorIntrinsic implements WasmIntrinsic {
|
|||
case "moveMemoryBlock": {
|
||||
MethodReference delegateMethod = new MethodReference(WasmRuntime.class.getName(),
|
||||
invocation.getMethod().getDescriptor());
|
||||
WasmCall call = new WasmCall(WasmMangling.mangleMethod(delegateMethod));
|
||||
WasmCall call = new WasmCall(Mangling.mangleMethod(delegateMethod));
|
||||
call.getArguments().addAll(invocation.getArguments().stream()
|
||||
.map(manager::generate)
|
||||
.collect(Collectors.toList()));
|
||||
|
@ -80,7 +80,7 @@ public class AllocatorIntrinsic implements WasmIntrinsic {
|
|||
WasmExpression flags = new WasmLoadInt32(4, pointer, WasmInt32Subtype.INT32);
|
||||
WasmExpression flag = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, flags,
|
||||
new WasmInt32Constant(RuntimeClass.INITIALIZED));
|
||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, flag, new WasmInt32Constant(0));
|
||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, flag, new WasmInt32Constant(0));
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||
|
|
|
@ -74,14 +74,12 @@ import org.teavm.model.MethodDescriptor;
|
|||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.instructions.ArrayElementType;
|
||||
import org.teavm.model.util.VariableType;
|
||||
|
||||
public class AstIO {
|
||||
private static final ElementModifier[] nodeModifiers = ElementModifier.values();
|
||||
private static final BinaryOperation[] binaryOperations = BinaryOperation.values();
|
||||
private static final UnaryOperation[] unaryOperations = UnaryOperation.values();
|
||||
private static final ArrayElementType[] arrayElementTypes = ArrayElementType.values();
|
||||
private final SymbolTable symbolTable;
|
||||
private final SymbolTable fileTable;
|
||||
private final Map<String, IdentifiedStatement> statementMap = new HashMap<>();
|
||||
|
@ -876,7 +874,7 @@ public class AstIO {
|
|||
return expr;
|
||||
}
|
||||
case 12: {
|
||||
UnwrapArrayExpr expr = new UnwrapArrayExpr(arrayElementTypes[input.readByte()]);
|
||||
UnwrapArrayExpr expr = new UnwrapArrayExpr(ArrayType.values()[input.readByte()]);
|
||||
expr.setArray(readExpr(input));
|
||||
return expr;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ public class ClassInitializerTransformer {
|
|||
checkInitialized.setReceiver(initializedVariable);
|
||||
block.add(checkInitialized);
|
||||
|
||||
BranchingInstruction branching = new BranchingInstruction(BranchingCondition.EQUAL);
|
||||
BranchingInstruction branching = new BranchingInstruction(BranchingCondition.NOT_EQUAL);
|
||||
branching.setOperand(initializedVariable);
|
||||
branching.setConsequent(continueBlock);
|
||||
branching.setAlternative(initBlock);
|
||||
|
|
|
@ -36,14 +36,15 @@ public class ManagedMethodRepository {
|
|||
}
|
||||
|
||||
private boolean computeIsManaged(MethodReference methodReference) {
|
||||
ClassReader cls = classSource.get(methodReference.getClassName());
|
||||
if (cls == null) {
|
||||
MethodReader method = classSource.resolve(methodReference);
|
||||
if (method == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ClassReader cls = classSource.get(method.getOwnerName());
|
||||
if (cls.getAnnotations().get(Unmanaged.class.getName()) != null) {
|
||||
return false;
|
||||
}
|
||||
MethodReader method = cls.getMethod(methodReference.getDescriptor());
|
||||
return method == null || method.getAnnotations().get(Unmanaged.class.getName()) == null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
* 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.
|
||||
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.patches;
|
||||
package org.teavm.model.transformation;
|
||||
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.BasicBlock;
|
|
@ -33,10 +33,10 @@ public final class Allocator {
|
|||
}
|
||||
|
||||
public static Address allocateArray(RuntimeClass tag, int size) {
|
||||
int itemSize = (tag.itemType.flags & RuntimeClass.PRIMITIVE) != 0 ? tag.itemType.size : 4;
|
||||
int itemSize = (tag.itemType.flags & RuntimeClass.PRIMITIVE) != 0 ? tag.itemType.size : Address.sizeOf();
|
||||
int sizeInBytes = Address.align(Address.fromInt(Structure.sizeOf(RuntimeArray.class)), itemSize).toInt();
|
||||
sizeInBytes += itemSize * size;
|
||||
sizeInBytes = Address.align(Address.fromInt(sizeInBytes), 4).toInt();
|
||||
sizeInBytes = Address.align(Address.fromInt(sizeInBytes), Address.sizeOf()).toInt();
|
||||
Address result = GC.alloc(sizeInBytes).toAddress();
|
||||
fillZero(result, sizeInBytes);
|
||||
|
||||
|
@ -47,6 +47,7 @@ public final class Allocator {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static RuntimeArray allocateMultiArray(RuntimeClass tag, Address dimensions, int dimensionCount) {
|
||||
int size = dimensions.getInt();
|
||||
RuntimeArray array = allocateArray(tag, dimensions.getInt()).toStructure();
|
||||
|
|
|
@ -15,8 +15,12 @@
|
|||
*/
|
||||
package org.teavm.runtime;
|
||||
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
|
||||
@Unmanaged
|
||||
@StaticInit
|
||||
public class CallSiteLocation extends Structure {
|
||||
public String fileName;
|
||||
public String className;
|
||||
|
|
|
@ -65,6 +65,11 @@ public final class ExceptionHandling {
|
|||
}
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void throwClassCastException() {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int callStackSize() {
|
||||
Address stackFrame = ShadowStack.getStackTop();
|
||||
|
@ -77,8 +82,8 @@ public final class ExceptionHandling {
|
|||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void fillStackTrace(StackTraceElement[] target, int skip) {
|
||||
Address stackFrame = ShadowStack.getNextStackFrame(ShadowStack.getNextStackFrame(ShadowStack.getStackTop()));
|
||||
public static void fillStackTrace(StackTraceElement[] target) {
|
||||
Address stackFrame = ShadowStack.getNextStackFrame(ShadowStack.getStackTop());
|
||||
int index = 0;
|
||||
while (stackFrame != null && index < target.length) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
|
@ -87,12 +92,7 @@ public final class ExceptionHandling {
|
|||
StackTraceElement element = createElement(location != null ? location.className : "",
|
||||
location != null ? location.methodName : "", location != null ? location.fileName : null,
|
||||
location != null ? location.lineNumber : -1);
|
||||
if (skip > 0) {
|
||||
skip--;
|
||||
} else {
|
||||
target[index++] = element;
|
||||
}
|
||||
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ public final class GC {
|
|||
|
||||
public static RuntimeObject alloc(int size) {
|
||||
FreeChunk current = currentChunk;
|
||||
Address next = currentChunk.toAddress().add(size);
|
||||
Address next = current.toAddress().add(size);
|
||||
if (!next.add(Structure.sizeOf(FreeChunk.class)).isLessThan(currentChunkLimit)) {
|
||||
getAvailableChunk(size);
|
||||
current = currentChunk;
|
||||
|
@ -125,7 +125,7 @@ public final class GC {
|
|||
|
||||
Address staticRoots = Mutator.getStaticGCRoots();
|
||||
int staticCount = staticRoots.getInt();
|
||||
staticRoots = staticRoots.add(8);
|
||||
staticRoots = staticRoots.add(Address.sizeOf());
|
||||
while (staticCount-- > 0) {
|
||||
RuntimeObject object = staticRoots.getAddress().getAddress().toStructure();
|
||||
if (object != null) {
|
||||
|
@ -187,13 +187,13 @@ public final class GC {
|
|||
} else {
|
||||
if ((cls.itemType.flags & RuntimeClass.PRIMITIVE) == 0) {
|
||||
RuntimeArray array = (RuntimeArray) object;
|
||||
Address base = Address.align(array.toAddress().add(RuntimeArray.class, 1), 4);
|
||||
Address base = Address.align(array.toAddress().add(RuntimeArray.class, 1), Address.sizeOf());
|
||||
for (int i = 0; i < array.size; ++i) {
|
||||
RuntimeObject reference = base.getAddress().toStructure();
|
||||
if (reference != null && !isMarked(reference)) {
|
||||
MarkQueue.enqueue(reference);
|
||||
}
|
||||
base = base.add(4);
|
||||
base = base.add(Address.sizeOf());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -248,6 +248,7 @@ public final class GC {
|
|||
}
|
||||
} else {
|
||||
if (lastFreeSpace != null) {
|
||||
lastFreeSpace.classReference = 0;
|
||||
lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
|
||||
freeChunkPtr.value = lastFreeSpace;
|
||||
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
|
||||
|
@ -266,6 +267,7 @@ public final class GC {
|
|||
|
||||
if (lastFreeSpace != null) {
|
||||
int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
|
||||
lastFreeSpace.classReference = 0;
|
||||
lastFreeSpace.size = freeSize;
|
||||
freeChunkPtr.value = lastFreeSpace;
|
||||
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
|
||||
|
@ -348,7 +350,7 @@ public final class GC {
|
|||
Address address = Address.fromInt(Structure.sizeOf(RuntimeArray.class));
|
||||
address = Address.align(address, itemSize);
|
||||
address = address.add(itemSize * array.size);
|
||||
address = Address.align(address, 4);
|
||||
address = Address.align(address, Address.sizeOf());
|
||||
return address.toInt();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,6 @@
|
|||
*/
|
||||
package org.teavm.runtime;
|
||||
|
||||
public class RuntimeArray extends RuntimeJavaObject {
|
||||
public class RuntimeArray extends RuntimeObject {
|
||||
public int size;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package org.teavm.runtime;
|
|||
import org.teavm.interop.Address;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
|
||||
public class RuntimeClass extends RuntimeJavaObject {
|
||||
public class RuntimeClass extends RuntimeObject {
|
||||
public static final int INITIALIZED = 1;
|
||||
public static final int PRIMITIVE = 2;
|
||||
public static final int ENUM = 4;
|
||||
|
@ -27,7 +27,7 @@ public class RuntimeClass extends RuntimeJavaObject {
|
|||
public int flags;
|
||||
public int tag;
|
||||
public int canary;
|
||||
public RuntimeJavaObject name;
|
||||
public RuntimeObject name;
|
||||
public RuntimeClass itemType;
|
||||
public RuntimeClass arrayType;
|
||||
public IsSupertypeFunction isSupertypeOf;
|
||||
|
|
|
@ -15,11 +15,15 @@
|
|||
*/
|
||||
package org.teavm.runtime;
|
||||
|
||||
import org.teavm.interop.StaticInit;
|
||||
import org.teavm.interop.Structure;
|
||||
|
||||
@StaticInit
|
||||
public class RuntimeObject extends Structure {
|
||||
public static final int GC_MARKED = 0x80000000;
|
||||
public static final int MONITOR_EXISTS = 0x20000000;
|
||||
|
||||
public static int nextId;
|
||||
|
||||
public int classReference;
|
||||
public int hashCode;
|
||||
}
|
||||
|
|
|
@ -236,6 +236,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
return diagnostics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPlatformTags() {
|
||||
return target.getPlatformTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds an entry point. TeaVM guarantees, that all methods that are required by the entry point
|
||||
* will be available at run-time in browser. Also you need to specify for each parameter of entry point
|
||||
|
|
|
@ -42,4 +42,6 @@ public interface TeaVMTarget {
|
|||
void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource);
|
||||
|
||||
void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException;
|
||||
|
||||
String[] getPlatformTags();
|
||||
}
|
||||
|
|
|
@ -58,4 +58,6 @@ public interface TeaVMHost {
|
|||
* visible to VM.
|
||||
*/
|
||||
Properties getProperties();
|
||||
|
||||
String[] getPlatformTags();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
static inline int32_t instanceof(void* obj, int32_t (*cls)(JavaClass*)) {
|
||||
return obj != NULL && cls(CLASS_OF(obj));
|
||||
}
|
||||
|
||||
static inline void* checkcast(void* obj, int32_t (*cls)(JavaClass*)) {
|
||||
return obj == NULL || cls(CLASS_OF(obj)) ? obj : throwClassCastException();
|
||||
}
|
119
core/src/main/resources/org/teavm/backend/c/runtime.c
Normal file
119
core/src/main/resources/org/teavm/backend/c/runtime.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <uchar.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
struct JavaObject;
|
||||
struct JavaArray;
|
||||
struct JavaClass;
|
||||
struct JavaString;
|
||||
typedef struct JavaObject JavaObject;
|
||||
typedef struct JavaArray JavaArray;
|
||||
typedef struct JavaClass JavaClass;
|
||||
typedef struct JavaString JavaString;
|
||||
|
||||
#define PACK_CLASS(cls) ((int32_t) (((uintptr_t) (cls)) >> 3))
|
||||
#define UNPACK_CLASS(cls) ((JavaClass *) (uintptr_t) (uint32_t) (uintptr_t) (((int64_t*) NULL) + cls))
|
||||
#define CLASS_OF(obj) (UNPACK_CLASS(((JavaObject*) (obj))->header))
|
||||
#define AS(ptr, type) ((type*) (ptr))
|
||||
|
||||
#define VTABLE(obj, type) (AS(CLASS_OF(obj), type))
|
||||
#define METHOD(obj, type, method) (VTABLE(obj, type)->method)
|
||||
#define FIELD(ptr, type, name) (AS(ptr, type)->name)
|
||||
|
||||
#define TO_BYTE(i) ((((i) << 24) >> 24))
|
||||
#define TO_SHORT(i) ((((i) << 16) >> 16))
|
||||
#define TO_CHAR(i) ((char16_t) (i))
|
||||
|
||||
static inline int32_t compare_i32(int32_t a, int32_t b) {
|
||||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
|
||||
}
|
||||
static inline int32_t compare_i64(int64_t a, int64_t b) {
|
||||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
|
||||
}
|
||||
static inline int32_t compare_float(float a, float b) {
|
||||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
|
||||
}
|
||||
static inline int32_t compare_double(double a, double b) {
|
||||
return a > b ? INT32_C(1) : a < b ? INT32_C(-1) : INT32_C(0);
|
||||
}
|
||||
|
||||
#define ALIGN(addr, alignment) ((void*) (((uintptr_t) (addr) + ((alignment) - 1)) / (alignment) * (alignment)))
|
||||
#define ARRAY_LENGTH(array) (((JavaArray*) (array))->size)
|
||||
#define ARRAY_DATA(array, type) ((type*) ALIGN((((JavaArray*) (array)) + 1), sizeof(type)))
|
||||
#define ARRAY_AT(array, type, index) (((type*) ARRAY_DATA(array, type))[index])
|
||||
|
||||
static void* throwClassCastException();
|
||||
static inline int32_t instanceof(void*, int32_t (*)(JavaClass*));
|
||||
static inline void* checkcast(void*, int32_t (*)(JavaClass*));
|
||||
|
||||
#define ALLOC_STACK(size) \
|
||||
void* __shadowStack__[(size) + 3]; \
|
||||
__shadowStack__[0] = stackTop; \
|
||||
__shadowStack__[2] = (void*) size; \
|
||||
stackTop = __shadowStack__
|
||||
|
||||
#define RELEASE_STACK stackTop = __shadowStack__[0]
|
||||
#define GC_ROOT(index, ptr) __shadowStack__[3 + (index)] = ptr
|
||||
#define GC_ROOT_RELEASE(index) __shadowStack__[3 + (index)] = NULL
|
||||
#define CALL_SITE(id) (__shadowStack__[1] = (void*) (id))
|
||||
#define EXCEPTION_HANDLER ((int32_t) (intptr_t) (__shadowStack__[1]))
|
||||
#define SET_EXCEPTION_HANDLER(frame, id) (((void**) (frame))[1] = (void*) (intptr_t) (id))
|
||||
|
||||
#define ADDRESS_ADD(address, offset) ((char *) (address) + (offset))
|
||||
#define STRUCTURE_ADD(structure, address, offset) (((structure*) (address)) + offset)
|
||||
|
||||
static void** stackTop;
|
||||
|
||||
static void* gc_gcStorageAddress = NULL;
|
||||
static int32_t gc_gcStorageSize = INT32_C(0);
|
||||
static void* gc_heapAddress = NULL;
|
||||
static void* gc_regionsAddress = NULL;
|
||||
static int32_t gc_regionSize = INT32_C(32768);
|
||||
static int32_t gc_regionMaxCount = INT32_C(0);
|
||||
static int64_t gc_availableBytes = INT64_C(0);
|
||||
|
||||
static void initHeap(long heapSize) {
|
||||
long workSize = heapSize / 16;
|
||||
long regionsSize = (long) (heapSize / gc_regionSize);
|
||||
|
||||
long pageSize = sysconf(_SC_PAGE_SIZE);
|
||||
int heapPages = (int) ((heapSize + pageSize + 1) / pageSize * pageSize);
|
||||
int workPages = (int) ((workSize + pageSize + 1) / pageSize * pageSize);
|
||||
int regionsPages = (int) ((regionsSize * 2 + pageSize + 1) / pageSize * pageSize);
|
||||
|
||||
gc_heapAddress = mmap(
|
||||
NULL,
|
||||
heapPages,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
0, 0);
|
||||
gc_gcStorageAddress = mmap(
|
||||
NULL,
|
||||
workPages,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
0, 0);
|
||||
gc_regionsAddress = mmap(
|
||||
NULL,
|
||||
regionsPages,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
0, 0);
|
||||
|
||||
gc_gcStorageSize = (int) workSize;
|
||||
gc_regionMaxCount = regionsSize;
|
||||
gc_availableBytes = heapSize;
|
||||
}
|
||||
|
||||
static int64_t currentTimeMillis() {
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_REALTIME, &time);
|
||||
|
||||
return time.tv_sec * 1000 + round(time.tv_nsec / 1000000);
|
||||
}
|
|
@ -23,4 +23,5 @@ import java.lang.annotation.Target;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD })
|
||||
public @interface PlatformMarker {
|
||||
String value() default "";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
public final class PlatformMarkers {
|
||||
private PlatformMarkers() {
|
||||
}
|
||||
|
||||
public static final String JAVASCRIPT = "javascript";
|
||||
public static final String WEBASSEMBLY = "webassembly";
|
||||
public static final String C = "c";
|
||||
public static final String LOW_LEVEL = "low_level";
|
||||
}
|
|
@ -37,6 +37,7 @@ public final class Platform {
|
|||
private static boolean newInstancePrepared;
|
||||
|
||||
@InjectedBy(PlatformGenerator.class)
|
||||
@Unmanaged
|
||||
public static native PlatformObject getPlatformObject(Object obj);
|
||||
|
||||
@GeneratedBy(PlatformGenerator.class)
|
||||
|
@ -80,6 +81,7 @@ public final class Platform {
|
|||
|
||||
@InjectedBy(PlatformGenerator.class)
|
||||
@PluggableDependency(PlatformGenerator.class)
|
||||
@Unmanaged
|
||||
public static native Class<?> asJavaClass(PlatformObject obj);
|
||||
|
||||
public static PlatformConsole getConsole() {
|
||||
|
@ -213,6 +215,7 @@ public final class Platform {
|
|||
}
|
||||
|
||||
@DelegateTo("getArrayItemLowLevel")
|
||||
@Unmanaged
|
||||
public static PlatformClass getArrayItem(PlatformClass cls) {
|
||||
return cls.getMetadata().getArrayItem();
|
||||
}
|
||||
|
|
|
@ -15,16 +15,20 @@
|
|||
*/
|
||||
package org.teavm.platform;
|
||||
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
|
||||
public interface PlatformClass extends JSObject {
|
||||
@JSProperty("$meta")
|
||||
@Unmanaged
|
||||
PlatformClassMetadata getMetadata();
|
||||
|
||||
@JSProperty("classObject")
|
||||
@Unmanaged
|
||||
void setJavaClass(PlatformObject obj);
|
||||
|
||||
@JSProperty("classObject")
|
||||
@Unmanaged
|
||||
PlatformObject getJavaClass();
|
||||
}
|
||||
|
|
|
@ -15,16 +15,20 @@
|
|||
*/
|
||||
package org.teavm.platform;
|
||||
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.jso.JSObject;
|
||||
import org.teavm.jso.JSProperty;
|
||||
|
||||
public interface PlatformObject extends JSObject {
|
||||
@JSProperty("constructor")
|
||||
@Unmanaged
|
||||
PlatformClass getPlatformClass();
|
||||
|
||||
@JSProperty("$id$")
|
||||
@Unmanaged
|
||||
int getId();
|
||||
|
||||
@JSProperty("$id$")
|
||||
@Unmanaged
|
||||
void setId(int id);
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ public final class TeaVMRunner {
|
|||
options.addOption(OptionBuilder
|
||||
.withArgName("target")
|
||||
.hasArg()
|
||||
.withDescription("target type (javascript/js, webassembly/wasm)")
|
||||
.withDescription("target type (javascript/js, webassembly/wasm, C)")
|
||||
.create('t'));
|
||||
options.addOption(OptionBuilder
|
||||
.withArgName("directory")
|
||||
|
@ -141,7 +141,13 @@ public final class TeaVMRunner {
|
|||
.withLongOpt("wasm-version")
|
||||
.withArgName("version")
|
||||
.hasArg()
|
||||
.withDescription("WebAssembly binary version (11, 12, 13)")
|
||||
.withDescription("WebAssembly binary version (currently, only 1 is supported)")
|
||||
.create());
|
||||
options.addOption(OptionBuilder
|
||||
.withLongOpt("min-heap")
|
||||
.withArgName("size")
|
||||
.hasArg()
|
||||
.withDescription("Minimum heap size in bytes (for C and WebAssembly)")
|
||||
.create());
|
||||
}
|
||||
|
||||
|
@ -179,6 +185,7 @@ public final class TeaVMRunner {
|
|||
parseIncrementalOptions();
|
||||
parseJavaScriptOptions();
|
||||
parseWasmOptions();
|
||||
parseHeap();
|
||||
|
||||
interactive = commandLine.hasOption('w');
|
||||
|
||||
|
@ -202,6 +209,9 @@ public final class TeaVMRunner {
|
|||
case "wasm":
|
||||
tool.setTargetType(TeaVMTargetType.WEBASSEMBLY);
|
||||
break;
|
||||
case "c":
|
||||
tool.setTargetType(TeaVMTargetType.C);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -318,6 +328,20 @@ public final class TeaVMRunner {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseHeap() {
|
||||
if (commandLine.hasOption("min-heap")) {
|
||||
int size;
|
||||
try {
|
||||
size = Integer.parseInt(commandLine.getOptionValue("min-heap"));
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.print("Wrong heap size");
|
||||
printUsage();
|
||||
return;
|
||||
}
|
||||
tool.setMinHeapSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUp() {
|
||||
tool.setLog(log);
|
||||
tool.getProperties().putAll(System.getProperties());
|
||||
|
|
|
@ -17,5 +17,6 @@ package org.teavm.tooling;
|
|||
|
||||
public enum TeaVMTargetType {
|
||||
JAVASCRIPT,
|
||||
WEBASSEMBLY
|
||||
WEBASSEMBLY,
|
||||
C
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.List;
|
|||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.teavm.backend.c.CTarget;
|
||||
import org.teavm.backend.javascript.JavaScriptTarget;
|
||||
import org.teavm.backend.javascript.rendering.RenderingManager;
|
||||
import org.teavm.backend.wasm.WasmTarget;
|
||||
|
@ -97,7 +98,9 @@ public class TeaVMTool implements BaseTeaVMTool {
|
|||
private JavaScriptTarget javaScriptTarget;
|
||||
private WasmTarget webAssemblyTarget;
|
||||
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
|
||||
private CTarget cTarget;
|
||||
private Set<File> generatedFiles = new HashSet<>();
|
||||
private int minHeapSize = 32 * (1 << 20);
|
||||
|
||||
public File getTargetDirectory() {
|
||||
return targetDirectory;
|
||||
|
@ -224,6 +227,10 @@ public class TeaVMTool implements BaseTeaVMTool {
|
|||
this.optimizationLevel = optimizationLevel;
|
||||
}
|
||||
|
||||
public void setMinHeapSize(int minHeapSize) {
|
||||
this.minHeapSize = minHeapSize;
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
@ -308,6 +315,8 @@ public class TeaVMTool implements BaseTeaVMTool {
|
|||
return prepareJavaScriptTarget();
|
||||
case WEBASSEMBLY:
|
||||
return prepareWebAssemblyTarget();
|
||||
case C:
|
||||
return prepareCTarget();
|
||||
}
|
||||
throw new IllegalStateException("Unknown target type: " + targetType);
|
||||
}
|
||||
|
@ -333,9 +342,16 @@ public class TeaVMTool implements BaseTeaVMTool {
|
|||
webAssemblyTarget.setCEmitted(debugInformationGenerated);
|
||||
webAssemblyTarget.setWastEmitted(debugInformationGenerated);
|
||||
webAssemblyTarget.setVersion(wasmVersion);
|
||||
webAssemblyTarget.setMinHeapSize(minHeapSize);
|
||||
return webAssemblyTarget;
|
||||
}
|
||||
|
||||
private CTarget prepareCTarget() {
|
||||
cTarget = new CTarget();
|
||||
cTarget.setMinHeapSize(minHeapSize);
|
||||
return cTarget;
|
||||
}
|
||||
|
||||
public void generate() throws TeaVMToolException {
|
||||
try {
|
||||
cancelled = false;
|
||||
|
@ -449,6 +465,8 @@ public class TeaVMTool implements BaseTeaVMTool {
|
|||
return "classes.js";
|
||||
case WEBASSEMBLY:
|
||||
return "classes.wasm";
|
||||
case C:
|
||||
return "classes.c";
|
||||
default:
|
||||
return "classes";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user