mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
WASM: add transformation that converts exception handling to explicit guard checks
This commit is contained in:
parent
8af1e3e66d
commit
b67d243ad4
|
@ -15,17 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.classlib.java.io.TConsole;
|
import org.teavm.classlib.java.io.TConsole;
|
||||||
import org.teavm.classlib.java.io.TInputStream;
|
import org.teavm.classlib.java.io.TInputStream;
|
||||||
import org.teavm.classlib.java.io.TPrintStream;
|
import org.teavm.classlib.java.io.TPrintStream;
|
||||||
import org.teavm.classlib.java.lang.reflect.TArray;
|
import org.teavm.classlib.java.lang.reflect.TArray;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.interop.NoGC;
|
import org.teavm.interop.Unmanaged;
|
||||||
import org.teavm.interop.Structure;
|
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
import org.teavm.runtime.RuntimeArray;
|
import org.teavm.runtime.RuntimeArray;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
|
@ -81,7 +80,7 @@ public final class TSystem extends TObject {
|
||||||
@DelegateTo("doArrayCopyLowLevel")
|
@DelegateTo("doArrayCopyLowLevel")
|
||||||
private static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
|
private static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
static void doArrayCopyLowLevel(RuntimeArray src, int srcPos, RuntimeArray dest, int destPos, int length) {
|
static void doArrayCopyLowLevel(RuntimeArray src, int srcPos, RuntimeArray dest, int destPos, int length) {
|
||||||
RuntimeClass type = RuntimeClass.getClass(src);
|
RuntimeClass type = RuntimeClass.getClass(src);
|
||||||
int itemSize = type.itemType.size;
|
int itemSize = type.itemType.size;
|
||||||
|
@ -99,7 +98,7 @@ public final class TSystem extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(SystemNativeGenerator.class)
|
@GeneratedBy(SystemNativeGenerator.class)
|
||||||
@DelegateTo(("currentTimeMillisLowLevel"))
|
@DelegateTo("currentTimeMillisLowLevel")
|
||||||
public static native long currentTimeMillis();
|
public static native long currentTimeMillis();
|
||||||
|
|
||||||
private static long currentTimeMillisLowLevel() {
|
private static long currentTimeMillisLowLevel() {
|
||||||
|
|
|
@ -15,11 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang.reflect;
|
package org.teavm.classlib.java.lang.reflect;
|
||||||
|
|
||||||
import org.teavm.classlib.java.lang.*;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
|
import org.teavm.classlib.java.lang.TArrayIndexOutOfBoundsException;
|
||||||
|
import org.teavm.classlib.java.lang.TClass;
|
||||||
|
import org.teavm.classlib.java.lang.TIllegalArgumentException;
|
||||||
|
import org.teavm.classlib.java.lang.TNegativeArraySizeException;
|
||||||
|
import org.teavm.classlib.java.lang.TNullPointerException;
|
||||||
|
import org.teavm.classlib.java.lang.TObject;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.interop.Unmanaged;
|
||||||
import org.teavm.interop.NoGC;
|
|
||||||
import org.teavm.platform.PlatformClass;
|
import org.teavm.platform.PlatformClass;
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
import org.teavm.runtime.RuntimeArray;
|
import org.teavm.runtime.RuntimeArray;
|
||||||
|
@ -61,7 +66,7 @@ public final class TArray extends TObject {
|
||||||
private static native TObject newInstanceImpl(PlatformClass componentType, int length);
|
private static native TObject newInstanceImpl(PlatformClass componentType, int length);
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@NoGC
|
@Unmanaged
|
||||||
private static RuntimeObject newInstanceLowLevel(RuntimeClass cls, int length) {
|
private static RuntimeObject newInstanceLowLevel(RuntimeClass cls, int length) {
|
||||||
return Allocator.allocateArray(cls.arrayType, length).toStructure();
|
return Allocator.allocateArray(cls.arrayType, length).toStructure();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,11 @@ package org.teavm.backend.wasm;
|
||||||
|
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.interop.NoGC;
|
|
||||||
import org.teavm.interop.StaticInit;
|
import org.teavm.interop.StaticInit;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
@StaticInit
|
@StaticInit
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public final class WasmRuntime {
|
public final class WasmRuntime {
|
||||||
public static Address stack = initStack();
|
public static Address stack = initStack();
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ public final class WasmRuntime {
|
||||||
|
|
||||||
public static Address allocStack(int size) {
|
public static Address allocStack(int size) {
|
||||||
Address result = stack.add(4);
|
Address result = stack.add(4);
|
||||||
stack = result.add(size << 2);
|
stack = result.add((size << 2) + 4);
|
||||||
stack.putInt(size);
|
stack.putInt(size);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,7 @@ public final class WasmRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Address getNextStackRoots(Address address) {
|
public static Address getNextStackRoots(Address address) {
|
||||||
int size = address.getInt() + 1;
|
int size = address.getInt() + 2;
|
||||||
Address result = address.add(-size * 4);
|
Address result = address.add(-size * 4);
|
||||||
if (result == initStack()) {
|
if (result == initStack()) {
|
||||||
result = null;
|
result = null;
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.teavm.backend.wasm.generate.WasmStringPool;
|
||||||
import org.teavm.backend.wasm.intrinsics.AddressIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.AddressIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.ClassIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.ClassIntrinsic;
|
||||||
|
import org.teavm.backend.wasm.intrinsics.ExceptionHandlingIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.GCIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.GCIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic;
|
||||||
|
@ -107,7 +108,7 @@ import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.MutatorIntrinsic;
|
import org.teavm.model.instructions.MutatorIntrinsic;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||||
import org.teavm.model.lowlevel.GcRootMaintainingTransformer;
|
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
import org.teavm.runtime.RuntimeArray;
|
import org.teavm.runtime.RuntimeArray;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
|
@ -126,7 +127,7 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
private boolean cEmitted;
|
private boolean cEmitted;
|
||||||
private ClassInitializerEliminator classInitializerEliminator;
|
private ClassInitializerEliminator classInitializerEliminator;
|
||||||
private ClassInitializerTransformer classInitializerTransformer;
|
private ClassInitializerTransformer classInitializerTransformer;
|
||||||
private GcRootMaintainingTransformer gcRootMaintainingTransformer;
|
private ShadowStackTransformer shadowStackTransformer;
|
||||||
private MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
|
private MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -134,7 +135,7 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||||
classInitializerTransformer = new ClassInitializerTransformer();
|
classInitializerTransformer = new ClassInitializerTransformer();
|
||||||
gcRootMaintainingTransformer = new GcRootMaintainingTransformer(controller.getUnprocessedClassSource());
|
shadowStackTransformer = new ShadowStackTransformer(controller.getUnprocessedClassSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -248,7 +249,7 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
|
|
||||||
classInitializerEliminator.apply(program);
|
classInitializerEliminator.apply(program);
|
||||||
classInitializerTransformer.transform(program);
|
classInitializerTransformer.transform(program);
|
||||||
gcRootMaintainingTransformer.apply(program, method);
|
shadowStackTransformer.apply(program, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean needsClinitCall(MethodReader method) {
|
private static boolean needsClinitCall(MethodReader method) {
|
||||||
|
@ -293,6 +294,7 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
context.addIntrinsic(gcIntrinsic);
|
context.addIntrinsic(gcIntrinsic);
|
||||||
MutatorIntrinsic mutatorIntrinsic = new MutatorIntrinsic();
|
MutatorIntrinsic mutatorIntrinsic = new MutatorIntrinsic();
|
||||||
context.addIntrinsic(mutatorIntrinsic);
|
context.addIntrinsic(mutatorIntrinsic);
|
||||||
|
context.addIntrinsic(new ExceptionHandlingIntrinsic());
|
||||||
|
|
||||||
WasmGenerator generator = new WasmGenerator(decompiler, classes,
|
WasmGenerator generator = new WasmGenerator(decompiler, classes,
|
||||||
context, classGenerator);
|
context, classGenerator);
|
||||||
|
@ -326,12 +328,13 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
initFunction.getBody().add(new WasmCall(WasmMangling.mangleInitializer(className)));
|
initFunction.getBody().add(new WasmCall(WasmMangling.mangleInitializer(className)));
|
||||||
}
|
}
|
||||||
module.add(initFunction);
|
module.add(initFunction);
|
||||||
module.setStartFunction(initFunction);
|
//module.setStartFunction(initFunction);
|
||||||
|
|
||||||
for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) {
|
for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) {
|
||||||
String mangledName = WasmMangling.mangleMethod(entryPoint.getReference());
|
String mangledName = WasmMangling.mangleMethod(entryPoint.getReference());
|
||||||
WasmFunction function = module.getFunctions().get(mangledName);
|
WasmFunction function = module.getFunctions().get(mangledName);
|
||||||
if (function != null) {
|
if (function != null) {
|
||||||
|
function.getBody().add(0, new WasmCall(initFunction.getName()));
|
||||||
function.setExportName(entryPoint.getPublicName());
|
function.setExportName(entryPoint.getPublicName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,7 @@ import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.classes.TagRegistry;
|
import org.teavm.model.classes.TagRegistry;
|
||||||
import org.teavm.model.classes.VirtualTableEntry;
|
import org.teavm.model.classes.VirtualTableEntry;
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
|
import org.teavm.runtime.ExceptionHandling;
|
||||||
import org.teavm.runtime.Mutator;
|
import org.teavm.runtime.Mutator;
|
||||||
import org.teavm.runtime.RuntimeArray;
|
import org.teavm.runtime.RuntimeArray;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
|
@ -797,6 +798,15 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
generateRemoveGcRoot(expr.getArguments().get(0));
|
generateRemoveGcRoot(expr.getArguments().get(0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (expr.getMethod().getClassName().equals(ExceptionHandling.class.getName())) {
|
||||||
|
switch (expr.getMethod().getName()) {
|
||||||
|
case "registerCallSite":
|
||||||
|
generateRegisterCallSite(expr.getArguments().get(0));
|
||||||
|
return;
|
||||||
|
case "getHandlerId":
|
||||||
|
generateGetHandlerId();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod());
|
WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod());
|
||||||
|
@ -912,6 +922,27 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
result = new WasmStoreInt32(4, new WasmInt32Constant(offset), oldValue, WasmInt32Subtype.INT32);
|
result = new WasmStoreInt32(4, new WasmInt32Constant(offset), oldValue, WasmInt32Subtype.INT32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateRegisterCallSite(Expr callSiteExpr) {
|
||||||
|
if (stackVariable == null) {
|
||||||
|
throw new IllegalStateException("Call to ExceptionHandling.registerCallSite must be dominated by "
|
||||||
|
+ "Mutator.allocStack");
|
||||||
|
}
|
||||||
|
|
||||||
|
callSiteExpr.acceptVisitor(this);
|
||||||
|
WasmExpression callSite = result;
|
||||||
|
|
||||||
|
result = new WasmStoreInt32(4, new WasmGetLocal(stackVariable), callSite, WasmInt32Subtype.INT32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateGetHandlerId() {
|
||||||
|
if (stackVariable == null) {
|
||||||
|
throw new IllegalStateException("Call to ExceptionHandling.getHandlerId must be dominated by "
|
||||||
|
+ "Mutator.allocStack");
|
||||||
|
}
|
||||||
|
|
||||||
|
result = new WasmLoadInt32(4, new WasmGetLocal(stackVariable), WasmInt32Subtype.INT32);
|
||||||
|
}
|
||||||
|
|
||||||
private void generateRegisterGcRoot(Expr slotExpr, Expr gcRootExpr) {
|
private void generateRegisterGcRoot(Expr slotExpr, Expr gcRootExpr) {
|
||||||
if (stackVariable == null) {
|
if (stackVariable == null) {
|
||||||
throw new IllegalStateException("Call to Mutator.registerGcRoot must be dominated by "
|
throw new IllegalStateException("Call to Mutator.registerGcRoot must be dominated by "
|
||||||
|
@ -946,9 +977,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
private WasmExpression getSlotOffset(WasmExpression slot) {
|
private WasmExpression getSlotOffset(WasmExpression slot) {
|
||||||
if (slot instanceof WasmInt32Constant) {
|
if (slot instanceof WasmInt32Constant) {
|
||||||
int slotConstant = ((WasmInt32Constant) slot).getValue();
|
int slotConstant = ((WasmInt32Constant) slot).getValue();
|
||||||
return new WasmInt32Constant(slotConstant << 2);
|
return new WasmInt32Constant((slotConstant << 2) + 4);
|
||||||
} else {
|
} else {
|
||||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, slot, new WasmInt32Constant(2));
|
slot = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, slot, new WasmInt32Constant(2));
|
||||||
|
slot = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, slot, new WasmInt32Constant(4));
|
||||||
|
return slot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.backend.wasm.intrinsics;
|
||||||
|
|
||||||
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.runtime.ExceptionHandling;
|
||||||
|
|
||||||
|
public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable(MethodReference methodReference) {
|
||||||
|
if (!methodReference.getClassName().equals(ExceptionHandling.class.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (methodReference.getName()) {
|
||||||
|
case "registerCallSite":
|
||||||
|
case "getHandlerId":
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "registerCallSite":
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,9 @@ import org.teavm.ast.InvocationExpr;
|
||||||
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||||
import org.teavm.interop.Structure;
|
import org.teavm.interop.Structure;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
@ -46,6 +49,19 @@ public class StructureIntrinsic implements WasmIntrinsic {
|
||||||
ValueType.Object type = (ValueType.Object) ((ConstantExpr) invocation.getArguments().get(0)).getValue();
|
ValueType.Object type = (ValueType.Object) ((ConstantExpr) invocation.getArguments().get(0)).getValue();
|
||||||
return new WasmInt32Constant(classGenerator.getClassSize(type.getClassName()));
|
return new WasmInt32Constant(classGenerator.getClassSize(type.getClassName()));
|
||||||
}
|
}
|
||||||
|
case "add": {
|
||||||
|
WasmExpression base = manager.generate(invocation.getArguments().get(1));
|
||||||
|
WasmExpression offset = manager.generate(invocation.getArguments().get(2));
|
||||||
|
Object type = ((ConstantExpr) invocation.getArguments().get(0)).getValue();
|
||||||
|
String className = ((ValueType.Object) type).getClassName();
|
||||||
|
int size = classGenerator.getClassSize(className);
|
||||||
|
int alignment = classGenerator.getClassAlignment(className);
|
||||||
|
size = WasmClassGenerator.align(size, alignment);
|
||||||
|
|
||||||
|
offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.MUL, offset,
|
||||||
|
new WasmInt32Constant(size));
|
||||||
|
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, base, offset);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException(invocation.getMethod().toString());
|
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CallSiteDescriptor {
|
||||||
|
private int id;
|
||||||
|
private List<ExceptionHandlerDescriptor> handlers = new ArrayList<>();
|
||||||
|
|
||||||
|
public CallSiteDescriptor(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ExceptionHandlerDescriptor> getHandlers() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
|
public class ExceptionHandlerDescriptor {
|
||||||
|
private int id;
|
||||||
|
private String className;
|
||||||
|
|
||||||
|
public ExceptionHandlerDescriptor(int id, String className) {
|
||||||
|
this.id = id;
|
||||||
|
this.className = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,335 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.common.DominatorTree;
|
||||||
|
import org.teavm.common.Graph;
|
||||||
|
import org.teavm.common.GraphUtils;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.Incoming;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Phi;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.TextLocation;
|
||||||
|
import org.teavm.model.TryCatchBlock;
|
||||||
|
import org.teavm.model.TryCatchJoint;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.InitClassInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.LongConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
|
import org.teavm.model.instructions.SwitchInstruction;
|
||||||
|
import org.teavm.model.instructions.SwitchTableEntry;
|
||||||
|
import org.teavm.model.util.DefinitionExtractor;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
import org.teavm.runtime.ExceptionHandling;
|
||||||
|
|
||||||
|
public class ExceptionHandlingShadowStackContributor {
|
||||||
|
private ManagedMethodRepository managedMethodRepository;
|
||||||
|
private List<CallSiteDescriptor> callSites;
|
||||||
|
private BasicBlock defaultExceptionHandler;
|
||||||
|
private MethodReference method;
|
||||||
|
private Program program;
|
||||||
|
private DominatorTree dom;
|
||||||
|
private BasicBlock[] variableDefinitionPlaces;
|
||||||
|
private Phi[] jointPhis;
|
||||||
|
|
||||||
|
public ExceptionHandlingShadowStackContributor(ManagedMethodRepository managedMethodRepository,
|
||||||
|
List<CallSiteDescriptor> callSites, MethodReference method, Program program) {
|
||||||
|
this.managedMethodRepository = managedMethodRepository;
|
||||||
|
this.callSites = callSites;
|
||||||
|
this.method = method;
|
||||||
|
this.program = program;
|
||||||
|
|
||||||
|
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||||
|
dom = GraphUtils.buildDominatorTree(cfg);
|
||||||
|
variableDefinitionPlaces = ProgramUtils.getVariableDefinitionPlaces(program);
|
||||||
|
jointPhis = new Phi[program.variableCount()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contribute() {
|
||||||
|
boolean hasExceptionHandlers = false;
|
||||||
|
int[] blockMapping = new int[program.basicBlockCount()];
|
||||||
|
for (int i = 0; i < blockMapping.length; ++i) {
|
||||||
|
blockMapping[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockCount = program.basicBlockCount();
|
||||||
|
for (int i = 0; i < blockCount; ++i) {
|
||||||
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
int newIndex = contributeToBasicBlock(block);
|
||||||
|
if (newIndex != i) {
|
||||||
|
blockMapping[i] = newIndex;
|
||||||
|
hasExceptionHandlers = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < blockCount; ++i) {
|
||||||
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
for (Phi phi : block.getPhis()) {
|
||||||
|
for (Incoming incoming : phi.getIncomings()) {
|
||||||
|
int mappedSource = blockMapping[incoming.getSource().getIndex()];
|
||||||
|
incoming.setSource(program.basicBlockAt(mappedSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasExceptionHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int contributeToBasicBlock(BasicBlock block) {
|
||||||
|
List<Instruction> instructions = block.getInstructions();
|
||||||
|
|
||||||
|
int[] currentJointSources = new int[program.variableCount()];
|
||||||
|
int[] jointReceiverMap = new int[program.variableCount()];
|
||||||
|
Arrays.fill(currentJointSources, -1);
|
||||||
|
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||||
|
for (TryCatchJoint joint : tryCatch.getJoints()) {
|
||||||
|
for (Variable sourceVar : joint.getSourceVariables()) {
|
||||||
|
BasicBlock sourceVarDefinedAt = variableDefinitionPlaces[sourceVar.getIndex()];
|
||||||
|
if (dom.dominates(sourceVarDefinedAt.getIndex(), block.getIndex())) {
|
||||||
|
currentJointSources[joint.getReceiver().getIndex()] = sourceVar.getIndex();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Variable sourceVar : joint.getSourceVariables()) {
|
||||||
|
jointReceiverMap[sourceVar.getIndex()] = joint.getReceiver().getIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DefinitionExtractor defExtractor = new DefinitionExtractor();
|
||||||
|
|
||||||
|
for (int i = 0; i < instructions.size(); ++i) {
|
||||||
|
Instruction insn = instructions.get(i);
|
||||||
|
|
||||||
|
if (isCallInstruction(insn)) {
|
||||||
|
BasicBlock next = program.createBasicBlock();
|
||||||
|
next.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
||||||
|
block.getTryCatchBlocks().clear();
|
||||||
|
|
||||||
|
List<Instruction> remainingInstructions = instructions.subList(i + 1, instructions.size());
|
||||||
|
List<Instruction> instructionsToMove = new ArrayList<>(remainingInstructions);
|
||||||
|
remainingInstructions.clear();
|
||||||
|
next.getInstructions().addAll(instructionsToMove);
|
||||||
|
|
||||||
|
CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size());
|
||||||
|
callSites.add(callSite);
|
||||||
|
List<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation());
|
||||||
|
List<Instruction> post = setLocation(
|
||||||
|
getInstructionsAfterCallSite(block, next, callSite, currentJointSources),
|
||||||
|
insn.getLocation());
|
||||||
|
instructions.addAll(instructions.size() - 1, pre);
|
||||||
|
instructions.addAll(post);
|
||||||
|
|
||||||
|
block = next;
|
||||||
|
instructions = block.getInstructions();
|
||||||
|
i = 0;
|
||||||
|
} else if (insn instanceof RaiseInstruction) {
|
||||||
|
InvokeInstruction raise = new InvokeInstruction();
|
||||||
|
raise.setMethod(new MethodReference(ExceptionHandling.class, "throwException", Throwable.class,
|
||||||
|
void.class));
|
||||||
|
raise.setType(InvocationType.SPECIAL);
|
||||||
|
raise.getArguments().add(((RaiseInstruction) insn).getException());
|
||||||
|
raise.setLocation(insn.getLocation());
|
||||||
|
instructions.add(i++, raise);
|
||||||
|
}
|
||||||
|
|
||||||
|
insn.acceptVisitor(defExtractor);
|
||||||
|
for (Variable definedVar : defExtractor.getDefinedVariables()) {
|
||||||
|
int jointReceiver = jointReceiverMap[definedVar.getIndex()];
|
||||||
|
currentJointSources[jointReceiver] = definedVar.getIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block.getTryCatchBlocks().clear();
|
||||||
|
|
||||||
|
return block.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCallInstruction(Instruction insn) {
|
||||||
|
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|
||||||
|
|| insn instanceof ConstructArrayInstruction || insn instanceof CloneArrayInstruction) {
|
||||||
|
return true;
|
||||||
|
} else if (insn instanceof InvokeInstruction) {
|
||||||
|
return managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Instruction> getInstructionsBeforeCallSite(CallSiteDescriptor callSite) {
|
||||||
|
List<Instruction> instructions = new ArrayList<>();
|
||||||
|
|
||||||
|
Variable idVariable = program.createVariable();
|
||||||
|
IntegerConstantInstruction idInsn = new IntegerConstantInstruction();
|
||||||
|
idInsn.setConstant(callSite.getId());
|
||||||
|
idInsn.setReceiver(idVariable);
|
||||||
|
instructions.add(idInsn);
|
||||||
|
|
||||||
|
InvokeInstruction registerInsn = new InvokeInstruction();
|
||||||
|
registerInsn.setMethod(new MethodReference(ExceptionHandling.class, "registerCallSite",
|
||||||
|
int.class, void.class));
|
||||||
|
registerInsn.setType(InvocationType.SPECIAL);
|
||||||
|
registerInsn.getArguments().add(idVariable);
|
||||||
|
instructions.add(registerInsn);
|
||||||
|
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Instruction> setLocation(List<Instruction> instructions, TextLocation location) {
|
||||||
|
if (location != null) {
|
||||||
|
for (Instruction instruction : instructions) {
|
||||||
|
instruction.setLocation(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Instruction> getInstructionsAfterCallSite(BasicBlock block, BasicBlock next,
|
||||||
|
CallSiteDescriptor callSite, int[] currentJointSources) {
|
||||||
|
Program program = block.getProgram();
|
||||||
|
List<Instruction> instructions = new ArrayList<>();
|
||||||
|
|
||||||
|
Variable handlerIdVariable = program.createVariable();
|
||||||
|
InvokeInstruction getHandlerIdInsn = new InvokeInstruction();
|
||||||
|
getHandlerIdInsn.setMethod(new MethodReference(ExceptionHandling.class, "getHandlerId", int.class));
|
||||||
|
getHandlerIdInsn.setType(InvocationType.SPECIAL);
|
||||||
|
getHandlerIdInsn.setReceiver(handlerIdVariable);
|
||||||
|
instructions.add(getHandlerIdInsn);
|
||||||
|
|
||||||
|
SwitchInstruction switchInsn = new SwitchInstruction();
|
||||||
|
switchInsn.setCondition(handlerIdVariable);
|
||||||
|
SwitchTableEntry continueExecutionEntry = new SwitchTableEntry();
|
||||||
|
continueExecutionEntry.setCondition(callSite.getId());
|
||||||
|
continueExecutionEntry.setTarget(next);
|
||||||
|
switchInsn.getEntries().add(continueExecutionEntry);
|
||||||
|
instructions.add(switchInsn);
|
||||||
|
|
||||||
|
boolean defaultExists = false;
|
||||||
|
int nextHandlerId = callSite.getId();
|
||||||
|
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||||
|
if (tryCatch.getExceptionType() == null) {
|
||||||
|
defaultExists = true;
|
||||||
|
switchInsn.setDefaultTarget(tryCatch.getHandler());
|
||||||
|
} else {
|
||||||
|
ExceptionHandlerDescriptor handler = new ExceptionHandlerDescriptor(++nextHandlerId,
|
||||||
|
tryCatch.getExceptionType());
|
||||||
|
callSite.getHandlers().add(handler);
|
||||||
|
|
||||||
|
SwitchTableEntry catchEntry = new SwitchTableEntry();
|
||||||
|
catchEntry.setTarget(tryCatch.getHandler());
|
||||||
|
catchEntry.setCondition(handler.getId());
|
||||||
|
switchInsn.getEntries().add(catchEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TryCatchJoint joint : tryCatch.getJoints()) {
|
||||||
|
Phi phi = getJointPhi(joint);
|
||||||
|
Incoming incoming = new Incoming();
|
||||||
|
incoming.setSource(block);
|
||||||
|
int value = currentJointSources[joint.getReceiver().getIndex()];
|
||||||
|
incoming.setValue(program.variableAt(value));
|
||||||
|
phi.getIncomings().add(incoming);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defaultExists) {
|
||||||
|
switchInsn.setDefaultTarget(getDefaultExceptionHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BasicBlock getDefaultExceptionHandler() {
|
||||||
|
if (defaultExceptionHandler == null) {
|
||||||
|
defaultExceptionHandler = program.createBasicBlock();
|
||||||
|
Variable result = createReturnValueInstructions(defaultExceptionHandler.getInstructions());
|
||||||
|
ExitInstruction exit = new ExitInstruction();
|
||||||
|
exit.setValueToReturn(result);
|
||||||
|
defaultExceptionHandler.getInstructions().add(exit);
|
||||||
|
}
|
||||||
|
return defaultExceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Phi getJointPhi(TryCatchJoint joint) {
|
||||||
|
Phi phi = jointPhis[joint.getReceiver().getIndex()];
|
||||||
|
if (phi == null) {
|
||||||
|
phi = new Phi();
|
||||||
|
phi.setReceiver(joint.getReceiver());
|
||||||
|
BasicBlock handler = program.basicBlockAt(joint.getBlock().getHandler().getIndex());
|
||||||
|
handler.getPhis().add(phi);
|
||||||
|
jointPhis[joint.getReceiver().getIndex()] = phi;
|
||||||
|
}
|
||||||
|
return phi;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Variable createReturnValueInstructions(List<Instruction> instructions) {
|
||||||
|
ValueType returnType = method.getReturnType();
|
||||||
|
if (returnType == ValueType.VOID) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Variable variable = program.createVariable();
|
||||||
|
|
||||||
|
if (returnType instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) returnType).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case BYTE:
|
||||||
|
case SHORT:
|
||||||
|
case CHARACTER:
|
||||||
|
case INTEGER:
|
||||||
|
IntegerConstantInstruction intConstant = new IntegerConstantInstruction();
|
||||||
|
intConstant.setReceiver(variable);
|
||||||
|
instructions.add(intConstant);
|
||||||
|
return variable;
|
||||||
|
case LONG:
|
||||||
|
LongConstantInstruction longConstant = new LongConstantInstruction();
|
||||||
|
longConstant.setReceiver(variable);
|
||||||
|
instructions.add(longConstant);
|
||||||
|
return variable;
|
||||||
|
case FLOAT:
|
||||||
|
FloatConstantInstruction floatConstant = new FloatConstantInstruction();
|
||||||
|
floatConstant.setReceiver(variable);
|
||||||
|
instructions.add(floatConstant);
|
||||||
|
return variable;
|
||||||
|
case DOUBLE:
|
||||||
|
DoubleConstantInstruction doubleConstant = new DoubleConstantInstruction();
|
||||||
|
doubleConstant.setReceiver(variable);
|
||||||
|
instructions.add(doubleConstant);
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NullConstantInstruction nullConstant = new NullConstantInstruction();
|
||||||
|
nullConstant.setReceiver(variable);
|
||||||
|
instructions.add(nullConstant);
|
||||||
|
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,10 +30,7 @@ import org.teavm.common.DominatorTree;
|
||||||
import org.teavm.common.Graph;
|
import org.teavm.common.Graph;
|
||||||
import org.teavm.common.GraphBuilder;
|
import org.teavm.common.GraphBuilder;
|
||||||
import org.teavm.common.GraphUtils;
|
import org.teavm.common.GraphUtils;
|
||||||
import org.teavm.interop.NoGC;
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassReader;
|
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.Incoming;
|
import org.teavm.model.Incoming;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
|
@ -44,12 +41,10 @@ import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
import org.teavm.model.instructions.ConstructInstruction;
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
import org.teavm.model.instructions.ExitInstruction;
|
|
||||||
import org.teavm.model.instructions.InitClassInstruction;
|
import org.teavm.model.instructions.InitClassInstruction;
|
||||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
|
||||||
import org.teavm.model.instructions.RaiseInstruction;
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
import org.teavm.model.util.DefinitionExtractor;
|
import org.teavm.model.util.DefinitionExtractor;
|
||||||
import org.teavm.model.util.GraphColorer;
|
import org.teavm.model.util.GraphColorer;
|
||||||
|
@ -60,17 +55,14 @@ import org.teavm.model.util.UsageExtractor;
|
||||||
import org.teavm.model.util.VariableType;
|
import org.teavm.model.util.VariableType;
|
||||||
import org.teavm.runtime.Mutator;
|
import org.teavm.runtime.Mutator;
|
||||||
|
|
||||||
public class GcRootMaintainingTransformer {
|
public class GCShadowStackContributor {
|
||||||
private ClassReaderSource classSource;
|
private ManagedMethodRepository managedMethodRepository;
|
||||||
|
|
||||||
public GcRootMaintainingTransformer(ClassReaderSource classSource) {
|
public GCShadowStackContributor(ManagedMethodRepository managedMethodRepository) {
|
||||||
this.classSource = classSource;
|
this.managedMethodRepository = managedMethodRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply(Program program, MethodReader method) {
|
public int contribute(Program program, MethodReader method) {
|
||||||
if (!requiresGC(method.getReference())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<IntObjectMap<BitSet>> liveInInformation = findCallSiteLiveIns(program, method);
|
List<IntObjectMap<BitSet>> liveInInformation = findCallSiteLiveIns(program, method);
|
||||||
|
|
||||||
Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program);
|
Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program);
|
||||||
|
@ -87,7 +79,7 @@ public class GcRootMaintainingTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (usedColors == 0) {
|
if (usedColors == 0) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a variable is spilled to stack, then phi which input this variable also spilled to stack
|
// If a variable is spilled to stack, then phi which input this variable also spilled to stack
|
||||||
|
@ -103,8 +95,8 @@ public class GcRootMaintainingTransformer {
|
||||||
List<IntObjectMap<int[]>> liveInStores = reduceGcRootStores(program, usedColors, liveInInformation,
|
List<IntObjectMap<int[]>> liveInStores = reduceGcRootStores(program, usedColors, liveInInformation,
|
||||||
colors, autoSpilled);
|
colors, autoSpilled);
|
||||||
putLiveInGcRoots(program, liveInStores);
|
putLiveInGcRoots(program, liveInStores);
|
||||||
addStackAllocation(program, usedColors);
|
|
||||||
addStackRelease(program, usedColors);
|
return usedColors;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findAutoSpilledPhis(boolean[] spilled, List<Set<Phi>> destinationPhis, int[] inputCount,
|
private void findAutoSpilledPhis(boolean[] spilled, List<Set<Phi>> destinationPhis, int[] inputCount,
|
||||||
|
@ -158,7 +150,8 @@ public class GcRootMaintainingTransformer {
|
||||||
if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction
|
if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction
|
||||||
|| insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction
|
|| insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction
|
||||||
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
|
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
|
||||||
if (insn instanceof InvokeInstruction && !requiresGC(((InvokeInstruction) insn).getMethod())) {
|
if (insn instanceof InvokeInstruction
|
||||||
|
&& !managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,86 +372,6 @@ public class GcRootMaintainingTransformer {
|
||||||
instructions.addAll(index, instructionsToAdd);
|
instructions.addAll(index, instructionsToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStackAllocation(Program program, int maxDepth) {
|
|
||||||
BasicBlock block = program.basicBlockAt(0);
|
|
||||||
List<Instruction> instructionsToAdd = new ArrayList<>();
|
|
||||||
Variable sizeVariable = program.createVariable();
|
|
||||||
|
|
||||||
IntegerConstantInstruction sizeConstant = new IntegerConstantInstruction();
|
|
||||||
sizeConstant.setReceiver(sizeVariable);
|
|
||||||
sizeConstant.setConstant(maxDepth);
|
|
||||||
instructionsToAdd.add(sizeConstant);
|
|
||||||
|
|
||||||
InvokeInstruction invocation = new InvokeInstruction();
|
|
||||||
invocation.setType(InvocationType.SPECIAL);
|
|
||||||
invocation.setMethod(new MethodReference(Mutator.class, "allocStack", int.class, void.class));
|
|
||||||
invocation.getArguments().add(sizeVariable);
|
|
||||||
instructionsToAdd.add(invocation);
|
|
||||||
|
|
||||||
block.getInstructions().addAll(0, instructionsToAdd);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addStackRelease(Program program, int maxDepth) {
|
|
||||||
List<BasicBlock> blocks = new ArrayList<>();
|
|
||||||
boolean hasResult = false;
|
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
|
||||||
Instruction instruction = block.getLastInstruction();
|
|
||||||
if (instruction instanceof ExitInstruction) {
|
|
||||||
blocks.add(block);
|
|
||||||
if (((ExitInstruction) instruction).getValueToReturn() != null) {
|
|
||||||
hasResult = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicBlock exitBlock;
|
|
||||||
if (blocks.size() == 1) {
|
|
||||||
exitBlock = blocks.get(0);
|
|
||||||
} else {
|
|
||||||
exitBlock = program.createBasicBlock();
|
|
||||||
ExitInstruction exit = new ExitInstruction();
|
|
||||||
exitBlock.getInstructions().add(exit);
|
|
||||||
|
|
||||||
if (hasResult) {
|
|
||||||
Phi phi = new Phi();
|
|
||||||
phi.setReceiver(program.createVariable());
|
|
||||||
exitBlock.getPhis().add(phi);
|
|
||||||
exit.setValueToReturn(phi.getReceiver());
|
|
||||||
|
|
||||||
for (BasicBlock block : blocks) {
|
|
||||||
ExitInstruction oldExit = (ExitInstruction) block.getLastInstruction();
|
|
||||||
Incoming incoming = new Incoming();
|
|
||||||
incoming.setSource(block);
|
|
||||||
incoming.setValue(oldExit.getValueToReturn());
|
|
||||||
phi.getIncomings().add(incoming);
|
|
||||||
|
|
||||||
JumpInstruction jumpToExit = new JumpInstruction();
|
|
||||||
jumpToExit.setTarget(exitBlock);
|
|
||||||
jumpToExit.setLocation(oldExit.getLocation());
|
|
||||||
|
|
||||||
block.getInstructions().set(block.getInstructions().size() - 1, jumpToExit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Instruction> instructionsToAdd = new ArrayList<>();
|
|
||||||
Variable sizeVariable = program.createVariable();
|
|
||||||
|
|
||||||
IntegerConstantInstruction sizeConstant = new IntegerConstantInstruction();
|
|
||||||
sizeConstant.setReceiver(sizeVariable);
|
|
||||||
sizeConstant.setConstant(maxDepth);
|
|
||||||
instructionsToAdd.add(sizeConstant);
|
|
||||||
|
|
||||||
InvokeInstruction invocation = new InvokeInstruction();
|
|
||||||
invocation.setType(InvocationType.SPECIAL);
|
|
||||||
invocation.setMethod(new MethodReference(Mutator.class, "releaseStack", int.class, void.class));
|
|
||||||
invocation.getArguments().add(sizeVariable);
|
|
||||||
instructionsToAdd.add(invocation);
|
|
||||||
|
|
||||||
exitBlock.getInstructions().addAll(exitBlock.getInstructions().size() - 1, instructionsToAdd);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isReference(TypeInferer typeInferer, int var) {
|
private boolean isReference(TypeInferer typeInferer, int var) {
|
||||||
VariableType liveType = typeInferer.typeOf(var);
|
VariableType liveType = typeInferer.typeOf(var);
|
||||||
switch (liveType) {
|
switch (liveType) {
|
||||||
|
@ -476,16 +389,4 @@ public class GcRootMaintainingTransformer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean requiresGC(MethodReference methodReference) {
|
|
||||||
ClassReader cls = classSource.get(methodReference.getClassName());
|
|
||||||
if (cls == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (cls.getAnnotations().get(NoGC.class.getName()) != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
MethodReader method = cls.getMethod(methodReference.getDescriptor());
|
|
||||||
return method.getAnnotations().get(NoGC.class.getName()) == null;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class ManagedMethodRepository {
|
||||||
|
private ClassReaderSource classSource;
|
||||||
|
private Map<MethodReference, Boolean> cache = new HashMap<>();
|
||||||
|
|
||||||
|
public ManagedMethodRepository(ClassReaderSource classSource) {
|
||||||
|
this.classSource = classSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isManaged(MethodReference methodReference) {
|
||||||
|
return cache.computeIfAbsent(methodReference, this::computeIsManaged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean computeIsManaged(MethodReference methodReference) {
|
||||||
|
ClassReader cls = classSource.get(methodReference.getClassName());
|
||||||
|
if (cls == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (cls.getAnnotations().get(Unmanaged.class.getName()) != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MethodReader method = cls.getMethod(methodReference.getDescriptor());
|
||||||
|
return method.getAnnotations().get(Unmanaged.class.getName()) == null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.Incoming;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Phi;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
|
import org.teavm.runtime.Mutator;
|
||||||
|
|
||||||
|
public class ShadowStackTransformer {
|
||||||
|
private ManagedMethodRepository managedMethodRepository;
|
||||||
|
private GCShadowStackContributor gcContributor;
|
||||||
|
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||||
|
|
||||||
|
public ShadowStackTransformer(ClassReaderSource classSource) {
|
||||||
|
managedMethodRepository = new ManagedMethodRepository(classSource);
|
||||||
|
gcContributor = new GCShadowStackContributor(managedMethodRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(Program program, MethodReader method) {
|
||||||
|
if (!managedMethodRepository.isManaged(method.getReference())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int shadowStackSize = gcContributor.contribute(program, method);
|
||||||
|
boolean exceptions = new ExceptionHandlingShadowStackContributor(managedMethodRepository, callSites,
|
||||||
|
method.getReference(), program).contribute();
|
||||||
|
|
||||||
|
if (shadowStackSize > 0 || exceptions) {
|
||||||
|
addStackAllocation(program, shadowStackSize);
|
||||||
|
addStackRelease(program, shadowStackSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStackAllocation(Program program, int maxDepth) {
|
||||||
|
BasicBlock block = program.basicBlockAt(0);
|
||||||
|
List<Instruction> instructionsToAdd = new ArrayList<>();
|
||||||
|
Variable sizeVariable = program.createVariable();
|
||||||
|
|
||||||
|
IntegerConstantInstruction sizeConstant = new IntegerConstantInstruction();
|
||||||
|
sizeConstant.setReceiver(sizeVariable);
|
||||||
|
sizeConstant.setConstant(maxDepth);
|
||||||
|
instructionsToAdd.add(sizeConstant);
|
||||||
|
|
||||||
|
InvokeInstruction invocation = new InvokeInstruction();
|
||||||
|
invocation.setType(InvocationType.SPECIAL);
|
||||||
|
invocation.setMethod(new MethodReference(Mutator.class, "allocStack", int.class, void.class));
|
||||||
|
invocation.getArguments().add(sizeVariable);
|
||||||
|
instructionsToAdd.add(invocation);
|
||||||
|
|
||||||
|
block.getInstructions().addAll(0, instructionsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStackRelease(Program program, int maxDepth) {
|
||||||
|
List<BasicBlock> blocks = new ArrayList<>();
|
||||||
|
boolean hasResult = false;
|
||||||
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
Instruction instruction = block.getLastInstruction();
|
||||||
|
if (instruction instanceof ExitInstruction) {
|
||||||
|
blocks.add(block);
|
||||||
|
if (((ExitInstruction) instruction).getValueToReturn() != null) {
|
||||||
|
hasResult = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicBlock exitBlock;
|
||||||
|
if (blocks.size() == 1) {
|
||||||
|
exitBlock = blocks.get(0);
|
||||||
|
} else {
|
||||||
|
exitBlock = program.createBasicBlock();
|
||||||
|
ExitInstruction exit = new ExitInstruction();
|
||||||
|
exitBlock.getInstructions().add(exit);
|
||||||
|
|
||||||
|
if (hasResult) {
|
||||||
|
Phi phi = new Phi();
|
||||||
|
phi.setReceiver(program.createVariable());
|
||||||
|
exitBlock.getPhis().add(phi);
|
||||||
|
exit.setValueToReturn(phi.getReceiver());
|
||||||
|
|
||||||
|
for (BasicBlock block : blocks) {
|
||||||
|
ExitInstruction oldExit = (ExitInstruction) block.getLastInstruction();
|
||||||
|
Incoming incoming = new Incoming();
|
||||||
|
incoming.setSource(block);
|
||||||
|
incoming.setValue(oldExit.getValueToReturn());
|
||||||
|
phi.getIncomings().add(incoming);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BasicBlock block : blocks) {
|
||||||
|
ExitInstruction oldExit = (ExitInstruction) block.getLastInstruction();
|
||||||
|
JumpInstruction jumpToExit = new JumpInstruction();
|
||||||
|
jumpToExit.setTarget(exitBlock);
|
||||||
|
jumpToExit.setLocation(oldExit.getLocation());
|
||||||
|
jumpToExit.setLocation(oldExit.getLocation());
|
||||||
|
|
||||||
|
block.getInstructions().set(block.getInstructions().size() - 1, jumpToExit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Instruction> instructionsToAdd = new ArrayList<>();
|
||||||
|
Variable sizeVariable = program.createVariable();
|
||||||
|
|
||||||
|
IntegerConstantInstruction sizeConstant = new IntegerConstantInstruction();
|
||||||
|
sizeConstant.setReceiver(sizeVariable);
|
||||||
|
sizeConstant.setConstant(maxDepth);
|
||||||
|
instructionsToAdd.add(sizeConstant);
|
||||||
|
|
||||||
|
InvokeInstruction invocation = new InvokeInstruction();
|
||||||
|
invocation.setType(InvocationType.SPECIAL);
|
||||||
|
invocation.setMethod(new MethodReference(Mutator.class, "releaseStack", int.class, void.class));
|
||||||
|
invocation.getArguments().add(sizeVariable);
|
||||||
|
instructionsToAdd.add(invocation);
|
||||||
|
|
||||||
|
exitBlock.getInstructions().addAll(exitBlock.getInstructions().size() - 1, instructionsToAdd);
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,8 +29,6 @@ import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Phi;
|
import org.teavm.model.Phi;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.TryCatchBlock;
|
|
||||||
import org.teavm.model.TryCatchJoint;
|
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||||
import org.teavm.model.instructions.BranchingInstruction;
|
import org.teavm.model.instructions.BranchingInstruction;
|
||||||
|
@ -42,7 +40,6 @@ import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
import org.teavm.model.instructions.SwitchInstruction;
|
import org.teavm.model.instructions.SwitchInstruction;
|
||||||
import org.teavm.model.util.BasicBlockMapper;
|
import org.teavm.model.util.BasicBlockMapper;
|
||||||
import org.teavm.model.util.InstructionCopyReader;
|
|
||||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
import org.teavm.model.util.InstructionTransitionExtractor;
|
||||||
import org.teavm.model.util.InstructionVariableMapper;
|
import org.teavm.model.util.InstructionVariableMapper;
|
||||||
import org.teavm.model.util.ProgramUtils;
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
@ -82,7 +79,7 @@ public class Inlining {
|
||||||
List<Instruction> instructionsToMove = new ArrayList<>(movedInstructions);
|
List<Instruction> instructionsToMove = new ArrayList<>(movedInstructions);
|
||||||
movedInstructions.clear();
|
movedInstructions.clear();
|
||||||
splitBlock.getInstructions().addAll(instructionsToMove);
|
splitBlock.getInstructions().addAll(instructionsToMove);
|
||||||
copyTryCatchBlocks(block, splitBlock);
|
splitBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
||||||
|
|
||||||
block.getInstructions().remove(block.getInstructions().size() - 1);
|
block.getInstructions().remove(block.getInstructions().size() - 1);
|
||||||
if (invoke.getInstance() == null || invoke.getMethod().getName().equals("<init>")) {
|
if (invoke.getInstance() == null || invoke.getMethod().getName().equals("<init>")) {
|
||||||
|
@ -97,7 +94,7 @@ public class Inlining {
|
||||||
for (int i = 0; i < inlineProgram.basicBlockCount(); ++i) {
|
for (int i = 0; i < inlineProgram.basicBlockCount(); ++i) {
|
||||||
BasicBlock blockToInline = inlineProgram.basicBlockAt(i);
|
BasicBlock blockToInline = inlineProgram.basicBlockAt(i);
|
||||||
BasicBlock inlineBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i);
|
BasicBlock inlineBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i);
|
||||||
copyInlinedBlock(blockToInline, inlineBlock);
|
ProgramUtils.copyBasicBlock(blockToInline, inlineBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicBlockMapper blockMapper = new BasicBlockMapper(index -> index + firstInlineBlock.getIndex());
|
BasicBlockMapper blockMapper = new BasicBlockMapper(index -> index + firstInlineBlock.getIndex());
|
||||||
|
@ -116,7 +113,7 @@ public class Inlining {
|
||||||
BasicBlock mappedBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i);
|
BasicBlock mappedBlock = program.basicBlockAt(firstInlineBlock.getIndex() + i);
|
||||||
blockMapper.transform(mappedBlock);
|
blockMapper.transform(mappedBlock);
|
||||||
variableMapper.apply(mappedBlock);
|
variableMapper.apply(mappedBlock);
|
||||||
copyTryCatchBlocks(block, mappedBlock);
|
mappedBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
||||||
Instruction lastInsn = mappedBlock.getLastInstruction();
|
Instruction lastInsn = mappedBlock.getLastInstruction();
|
||||||
if (lastInsn instanceof ExitInstruction) {
|
if (lastInsn instanceof ExitInstruction) {
|
||||||
ExitInstruction exit = (ExitInstruction) lastInsn;
|
ExitInstruction exit = (ExitInstruction) lastInsn;
|
||||||
|
@ -165,58 +162,6 @@ public class Inlining {
|
||||||
execPlan(program, planEntry.innerPlan, firstInlineBlock.getIndex());
|
execPlan(program, planEntry.innerPlan, firstInlineBlock.getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyInlinedBlock(BasicBlock source, BasicBlock target) {
|
|
||||||
if (source.getExceptionVariable() != null) {
|
|
||||||
target.setExceptionVariable(source.getExceptionVariable());
|
|
||||||
}
|
|
||||||
|
|
||||||
InstructionCopyReader insnCopier = new InstructionCopyReader(target.getProgram());
|
|
||||||
for (int i = 0; i < source.instructionCount(); ++i) {
|
|
||||||
source.readInstruction(i, insnCopier);
|
|
||||||
Instruction insn = insnCopier.getCopy();
|
|
||||||
target.getInstructions().add(insn);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Phi phi : source.getPhis()) {
|
|
||||||
Phi phiCopy = new Phi();
|
|
||||||
phiCopy.setReceiver(phi.getReceiver());
|
|
||||||
for (Incoming incoming : phi.getIncomings()) {
|
|
||||||
Incoming incomingCopy = new Incoming();
|
|
||||||
int sourceIndex = incoming.getSource().getIndex();
|
|
||||||
incomingCopy.setSource(target.getProgram().basicBlockAt(sourceIndex));
|
|
||||||
incomingCopy.setValue(incoming.getValue());
|
|
||||||
phiCopy.getIncomings().add(incomingCopy);
|
|
||||||
}
|
|
||||||
target.getPhis().add(phiCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (TryCatchBlock tryCatch : source.getTryCatchBlocks()) {
|
|
||||||
TryCatchBlock tryCatchCopy = new TryCatchBlock();
|
|
||||||
int handler = tryCatch.getHandler().getIndex();
|
|
||||||
tryCatchCopy.setExceptionType(tryCatch.getExceptionType());
|
|
||||||
tryCatchCopy.setHandler(target.getProgram().basicBlockAt(handler));
|
|
||||||
target.getTryCatchBlocks().add(tryCatchCopy);
|
|
||||||
|
|
||||||
for (TryCatchJoint joint : tryCatch.getJoints()) {
|
|
||||||
TryCatchJoint jointCopy = new TryCatchJoint();
|
|
||||||
jointCopy.setReceiver(joint.getReceiver());
|
|
||||||
jointCopy.getSourceVariables().addAll(joint.getSourceVariables());
|
|
||||||
tryCatchCopy.getJoints().add(joint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void copyTryCatchBlocks(BasicBlock source, BasicBlock target) {
|
|
||||||
List<TryCatchBlock> copiedTryCatches = new ArrayList<>();
|
|
||||||
for (TryCatchBlock tryCatch : source.getTryCatchBlocks()) {
|
|
||||||
TryCatchBlock tryCatchCopy = new TryCatchBlock();
|
|
||||||
tryCatchCopy.setExceptionType(tryCatch.getExceptionType());
|
|
||||||
tryCatchCopy.setHandler(tryCatch.getHandler());
|
|
||||||
copiedTryCatches.add(tryCatchCopy);
|
|
||||||
}
|
|
||||||
target.getTryCatchBlocks().addAll(copiedTryCatches);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<PlanEntry> buildPlan(Program program, ClassReaderSource classSource, int depth) {
|
private List<PlanEntry> buildPlan(Program program, ClassReaderSource classSource, int depth) {
|
||||||
if (depth >= MAX_DEPTH) {
|
if (depth >= MAX_DEPTH) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
|
@ -78,16 +78,22 @@ public final class ProgramUtils {
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlockReader block = program.basicBlockAt(i);
|
BasicBlockReader block = program.basicBlockAt(i);
|
||||||
BasicBlock blockCopy = copy.basicBlockAt(i);
|
BasicBlock blockCopy = copy.basicBlockAt(i);
|
||||||
if (block.getExceptionVariable() != null) {
|
copyBasicBlock(block, blockCopy);
|
||||||
blockCopy.setExceptionVariable(copy.variableAt(block.getExceptionVariable().getIndex()));
|
|
||||||
}
|
|
||||||
blockCopy.getInstructions().addAll(copyInstructions(block, 0, block.instructionCount(), copy));
|
|
||||||
blockCopy.getPhis().addAll(copyPhis(block, copy));
|
|
||||||
blockCopy.getTryCatchBlocks().addAll(copyTryCatches(block, copy));
|
|
||||||
}
|
}
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void copyBasicBlock(BasicBlockReader block, BasicBlock target) {
|
||||||
|
Program targetProgram = target.getProgram();
|
||||||
|
|
||||||
|
if (block.getExceptionVariable() != null) {
|
||||||
|
target.setExceptionVariable(targetProgram.variableAt(block.getExceptionVariable().getIndex()));
|
||||||
|
}
|
||||||
|
target.getInstructions().addAll(copyInstructions(block, 0, block.instructionCount(), targetProgram));
|
||||||
|
target.getPhis().addAll(copyPhis(block, targetProgram));
|
||||||
|
target.getTryCatchBlocks().addAll(copyTryCatches(block, targetProgram));
|
||||||
|
}
|
||||||
|
|
||||||
public static List<Instruction> copyInstructions(BasicBlockReader block, int from, int to, Program target) {
|
public static List<Instruction> copyInstructions(BasicBlockReader block, int from, int to, Program target) {
|
||||||
List<Instruction> result = new ArrayList<>();
|
List<Instruction> result = new ArrayList<>();
|
||||||
InstructionCopyReader copyReader = new InstructionCopyReader(target);
|
InstructionCopyReader copyReader = new InstructionCopyReader(target);
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
package org.teavm.runtime;
|
package org.teavm.runtime;
|
||||||
|
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.NoGC;
|
|
||||||
import org.teavm.interop.StaticInit;
|
import org.teavm.interop.StaticInit;
|
||||||
import org.teavm.interop.Structure;
|
import org.teavm.interop.Structure;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
@StaticInit
|
@StaticInit
|
||||||
public final class Allocator {
|
public final class Allocator {
|
||||||
|
@ -47,12 +47,12 @@ public final class Allocator {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public static native void fillZero(Address address, int count);
|
public static native void fillZero(Address address, int count);
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public static native void moveMemoryBlock(Address source, Address target, int count);
|
public static native void moveMemoryBlock(Address source, Address target, int count);
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public static native boolean isInitialized(Class<?> cls);
|
public static native boolean isInitialized(Class<?> cls);
|
||||||
}
|
}
|
||||||
|
|
27
core/src/main/java/org/teavm/runtime/CallSite.java
Normal file
27
core/src/main/java/org/teavm/runtime/CallSite.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 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.runtime;
|
||||||
|
|
||||||
|
import org.teavm.interop.StaticInit;
|
||||||
|
import org.teavm.interop.Structure;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
@StaticInit
|
||||||
|
public class CallSite extends Structure {
|
||||||
|
public int handlerCount;
|
||||||
|
public ExceptionHandler firstHandler;
|
||||||
|
}
|
27
core/src/main/java/org/teavm/runtime/ExceptionHandler.java
Normal file
27
core/src/main/java/org/teavm/runtime/ExceptionHandler.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 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.runtime;
|
||||||
|
|
||||||
|
import org.teavm.interop.StaticInit;
|
||||||
|
import org.teavm.interop.Structure;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
@StaticInit
|
||||||
|
public class ExceptionHandler extends Structure {
|
||||||
|
public int id;
|
||||||
|
public RuntimeClass exceptionClass;
|
||||||
|
}
|
69
core/src/main/java/org/teavm/runtime/ExceptionHandling.java
Normal file
69
core/src/main/java/org/teavm/runtime/ExceptionHandling.java
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 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.runtime;
|
||||||
|
|
||||||
|
import org.teavm.interop.Address;
|
||||||
|
import org.teavm.interop.StaticInit;
|
||||||
|
import org.teavm.interop.Structure;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
@StaticInit
|
||||||
|
public final class ExceptionHandling {
|
||||||
|
private ExceptionHandling() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native void registerCallSite(int id);
|
||||||
|
|
||||||
|
public static native int callSiteResult();
|
||||||
|
|
||||||
|
public static native Address getStackTop();
|
||||||
|
|
||||||
|
public static native Address getNextStackFrame(Address stackFrame);
|
||||||
|
|
||||||
|
public static native int getCallSiteId(Address stackFrame);
|
||||||
|
|
||||||
|
public static native void setHandlerId(Address stackFrame, int id);
|
||||||
|
|
||||||
|
public static native int getHandlerId();
|
||||||
|
|
||||||
|
public static native CallSite findCallSiteById(int id);
|
||||||
|
|
||||||
|
public static void throwException(Throwable exception) {
|
||||||
|
RuntimeObject exceptionPtr = Address.ofObject(exception).toStructure();
|
||||||
|
RuntimeClass exceptionClass = RuntimeClass.getClass(exceptionPtr);
|
||||||
|
IsSupertypeFunction isExceptionSupertype = exceptionClass.isSupertypeOf;
|
||||||
|
|
||||||
|
Address stackFrame = getStackTop();
|
||||||
|
stackLoop: while (stackFrame != null) {
|
||||||
|
int callSiteId = getCallSiteId(stackFrame);
|
||||||
|
CallSite callSite = findCallSiteById(callSiteId);
|
||||||
|
ExceptionHandler handler = callSite.firstHandler;
|
||||||
|
|
||||||
|
for (int i = 0; i < callSite.handlerCount; ++i) {
|
||||||
|
if (isExceptionSupertype.apply(handler.exceptionClass)) {
|
||||||
|
setHandlerId(stackFrame, handler.id);
|
||||||
|
break stackLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = Structure.add(ExceptionHandler.class, handler, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
setHandlerId(stackFrame, callSiteId - 1);
|
||||||
|
stackFrame = getNextStackFrame(stackFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,11 +16,11 @@
|
||||||
package org.teavm.runtime;
|
package org.teavm.runtime;
|
||||||
|
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.NoGC;
|
|
||||||
import org.teavm.interop.StaticInit;
|
import org.teavm.interop.StaticInit;
|
||||||
import org.teavm.interop.Structure;
|
import org.teavm.interop.Structure;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
@StaticInit
|
@StaticInit
|
||||||
public final class GC {
|
public final class GC {
|
||||||
private GC() {
|
private GC() {
|
||||||
|
@ -93,7 +93,7 @@ public final class GC {
|
||||||
if (--freeChunks == 0) {
|
if (--freeChunks == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
currentChunkPointer = currentChunkPointer.toAddress().add(FreeChunkHolder.class, 1).toStructure();
|
currentChunkPointer = Structure.add(FreeChunkHolder.class, currentChunkPointer, 1);
|
||||||
currentChunk = currentChunkPointer.value;
|
currentChunk = currentChunkPointer.value;
|
||||||
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
|
currentChunkLimit = currentChunk.toAddress().add(currentChunk.size);
|
||||||
}
|
}
|
||||||
|
@ -147,8 +147,7 @@ public final class GC {
|
||||||
object.classReference |= RuntimeObject.GC_MARKED;
|
object.classReference |= RuntimeObject.GC_MARKED;
|
||||||
|
|
||||||
long offset = object.toAddress().toLong() - heapAddress().toLong();
|
long offset = object.toAddress().toLong() - heapAddress().toLong();
|
||||||
Region region = regionsAddress().toAddress().add(Region.class, (int) (offset / regionSize()))
|
Region region = Structure.add(Region.class, regionsAddress(), (int) (offset / regionSize()));
|
||||||
.toStructure();
|
|
||||||
short relativeOffset = (short) (offset % regionSize() + 1);
|
short relativeOffset = (short) (offset % regionSize() + 1);
|
||||||
if (region.start == 0 || region.start > relativeOffset) {
|
if (region.start == 0 || region.start > relativeOffset) {
|
||||||
region.start = relativeOffset;
|
region.start = relativeOffset;
|
||||||
|
@ -221,16 +220,14 @@ public final class GC {
|
||||||
|
|
||||||
if (!object.toAddress().isLessThan(currentRegionEnd)) {
|
if (!object.toAddress().isLessThan(currentRegionEnd)) {
|
||||||
currentRegionIndex = (int) ((object.toAddress().toLong() - heapAddress().toLong()) / regionSize());
|
currentRegionIndex = (int) ((object.toAddress().toLong() - heapAddress().toLong()) / regionSize());
|
||||||
Region currentRegion = regionsAddress().toAddress().add(Region.class, currentRegionIndex)
|
Region currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex);
|
||||||
.toStructure();
|
|
||||||
if (currentRegion.start == 0) {
|
if (currentRegion.start == 0) {
|
||||||
do {
|
do {
|
||||||
if (++currentRegionIndex == regionsCount) {
|
if (++currentRegionIndex == regionsCount) {
|
||||||
object = limit.toStructure();
|
object = limit.toStructure();
|
||||||
break loop;
|
break loop;
|
||||||
}
|
}
|
||||||
currentRegion = regionsAddress().toAddress().add(Region.class, currentRegionIndex)
|
currentRegion = Structure.add(Region.class, regionsAddress(), currentRegionIndex);
|
||||||
.toStructure();
|
|
||||||
} while (currentRegion.start == 0);
|
} while (currentRegion.start == 0);
|
||||||
}
|
}
|
||||||
currentRegionEnd = currentRegion.toAddress().add(regionSize());
|
currentRegionEnd = currentRegion.toAddress().add(regionSize());
|
||||||
|
@ -239,7 +236,7 @@ public final class GC {
|
||||||
if (lastFreeSpace != null) {
|
if (lastFreeSpace != null) {
|
||||||
lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
|
lastFreeSpace.size = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
|
||||||
freeChunkPtr.value = lastFreeSpace;
|
freeChunkPtr.value = lastFreeSpace;
|
||||||
freeChunkPtr = freeChunkPtr.toAddress().add(FreeChunkHolder.class, 1).toStructure();
|
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
|
||||||
freeChunks++;
|
freeChunks++;
|
||||||
reclaimedSpace += lastFreeSpace.size;
|
reclaimedSpace += lastFreeSpace.size;
|
||||||
if (maxFreeChunk < lastFreeSpace.size) {
|
if (maxFreeChunk < lastFreeSpace.size) {
|
||||||
|
@ -257,7 +254,7 @@ public final class GC {
|
||||||
int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
|
int freeSize = (int) (object.toAddress().toLong() - lastFreeSpace.toAddress().toLong());
|
||||||
lastFreeSpace.size = freeSize;
|
lastFreeSpace.size = freeSize;
|
||||||
freeChunkPtr.value = lastFreeSpace;
|
freeChunkPtr.value = lastFreeSpace;
|
||||||
freeChunkPtr = freeChunkPtr.toAddress().add(FreeChunkHolder.class, 1).toStructure();
|
freeChunkPtr = Structure.add(FreeChunkHolder.class, freeChunkPtr, 1);
|
||||||
freeChunks++;
|
freeChunks++;
|
||||||
reclaimedSpace += freeSize;
|
reclaimedSpace += freeSize;
|
||||||
if (maxFreeChunk < freeSize) {
|
if (maxFreeChunk < freeSize) {
|
||||||
|
@ -310,7 +307,7 @@ public final class GC {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FreeChunkHolder getFreeChunk(int index) {
|
private static FreeChunkHolder getFreeChunk(int index) {
|
||||||
return currentChunkPointer.toAddress().add(FreeChunkHolder.class, index).toStructure();
|
return Structure.add(FreeChunkHolder.class, currentChunkPointer, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int objectSize(RuntimeObject object) {
|
private static int objectSize(RuntimeObject object) {
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
package org.teavm.runtime;
|
package org.teavm.runtime;
|
||||||
|
|
||||||
import org.teavm.interop.Function;
|
import org.teavm.interop.Function;
|
||||||
import org.teavm.interop.NoGC;
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
public abstract class IsSupertypeFunction extends Function {
|
public abstract class IsSupertypeFunction extends Function {
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public abstract boolean apply(RuntimeClass superType);
|
public abstract boolean apply(RuntimeClass superType);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
package org.teavm.runtime;
|
package org.teavm.runtime;
|
||||||
|
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.NoGC;
|
import org.teavm.interop.StaticInit;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
|
@StaticInit
|
||||||
public final class Mutator {
|
public final class Mutator {
|
||||||
private Mutator() {
|
private Mutator() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package org.teavm.runtime;
|
package org.teavm.runtime;
|
||||||
|
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.NoGC;
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
public class RuntimeClass extends RuntimeJavaObject {
|
public class RuntimeClass extends RuntimeJavaObject {
|
||||||
public static final int INITIALIZED = 1;
|
public static final int INITIALIZED = 1;
|
||||||
|
@ -32,17 +32,17 @@ public class RuntimeClass extends RuntimeJavaObject {
|
||||||
public RuntimeClass parent;
|
public RuntimeClass parent;
|
||||||
public Address layout;
|
public Address layout;
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public static int computeCanary(int size, int tag) {
|
public static int computeCanary(int size, int tag) {
|
||||||
return size ^ (tag << 8) ^ (tag >>> 24) ^ 0xAAAAAAAA;
|
return size ^ (tag << 8) ^ (tag >>> 24) ^ 0xAAAAAAAA;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public int computeCanary() {
|
public int computeCanary() {
|
||||||
return computeCanary(size, tag);
|
return computeCanary(size, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public static RuntimeClass getClass(RuntimeObject object) {
|
public static RuntimeClass getClass(RuntimeObject object) {
|
||||||
return Address.fromInt(object.classReference << 3).toStructure();
|
return Address.fromInt(object.classReference << 3).toStructure();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package org.teavm.interop;
|
package org.teavm.interop;
|
||||||
|
|
||||||
@StaticInit
|
@StaticInit
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public final class Address {
|
public final class Address {
|
||||||
public native Address add(int offset);
|
public native Address add(int offset);
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.interop;
|
package org.teavm.interop;
|
||||||
|
|
||||||
@NoGC
|
@Unmanaged
|
||||||
public class Structure {
|
public class Structure {
|
||||||
public final native <T extends Structure> T cast();
|
public final native <T extends Structure> T cast();
|
||||||
|
|
||||||
public final native Address toAddress();
|
public final native Address toAddress();
|
||||||
|
|
||||||
public static native int sizeOf(Class<? extends Structure> type);
|
public static native int sizeOf(Class<? extends Structure> type);
|
||||||
|
|
||||||
|
public static native <T extends Structure> T add(Class<T> type, T base, int offset);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,5 @@ import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||||
public @interface NoGC {
|
public @interface Unmanaged {
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user