C backend: initial commit

This commit is contained in:
Alexey Andreev 2018-02-09 00:10:25 +03:00
parent 540ad7f694
commit 05d0220dcd
74 changed files with 4449 additions and 110 deletions

View File

@ -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="" />

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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:

View 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 };
}
}

View 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());
}
}

View File

@ -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 {
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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--;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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";
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
};
}

View File

@ -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(";");
}
}
}

View 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;
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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("\"");
}
}

View File

@ -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(")");
}
}

View File

@ -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;
}
}
}

View 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.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;
}
}
}

View File

@ -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("))");
}
}

View 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.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());
}
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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":
}
}
}

View File

@ -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;
}
}
}

View File

@ -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(")");
}
}

View File

@ -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;
}
}

View File

@ -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 };
}
}

View File

@ -192,6 +192,7 @@ public final class Example {
throwsException();
} catch (IllegalStateException e) {
System.out.println("Caught 1: " + e.getMessage());
e.printStackTrace();
}
int x = 0;

View File

@ -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 };
}
}

View File

@ -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;
});
}

View File

@ -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;

View File

@ -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) {

View File

@ -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()));

View File

@ -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());

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;
}
target[index++] = element;
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
}
}

View File

@ -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();
}
}

View File

@ -15,6 +15,6 @@
*/
package org.teavm.runtime;
public class RuntimeArray extends RuntimeJavaObject {
public class RuntimeArray extends RuntimeObject {
public int size;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -58,4 +58,6 @@ public interface TeaVMHost {
* visible to VM.
*/
Properties getProperties();
String[] getPlatformTags();
}

View File

@ -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();
}

View 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);
}

View File

@ -23,4 +23,5 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface PlatformMarker {
String value() default "";
}

View File

@ -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";
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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());

View File

@ -17,5 +17,6 @@ package org.teavm.tooling;
public enum TeaVMTargetType {
JAVASCRIPT,
WEBASSEMBLY
WEBASSEMBLY,
C
}

View File

@ -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";
}