org.robovm.compiler.Linker Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2012 RoboVM AB
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.robovm.compiler;
import static org.robovm.compiler.Access.*;
import static org.robovm.compiler.Functions.*;
import static org.robovm.compiler.Symbols.*;
import static org.robovm.compiler.Types.*;
import static org.robovm.compiler.llvm.Linkage.*;
import static org.robovm.compiler.llvm.Type.*;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.clazz.ClazzInfo;
import org.robovm.compiler.clazz.MethodInfo;
import org.robovm.compiler.clazz.Path;
import org.robovm.compiler.config.Arch;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.config.OS;
import org.robovm.compiler.hash.HashTableGenerator;
import org.robovm.compiler.hash.ModifiedUtf8HashFunction;
import org.robovm.compiler.llvm.Alias;
import org.robovm.compiler.llvm.ArrayConstant;
import org.robovm.compiler.llvm.ArrayConstantBuilder;
import org.robovm.compiler.llvm.Constant;
import org.robovm.compiler.llvm.ConstantBitcast;
import org.robovm.compiler.llvm.ConstantGetelementptr;
import org.robovm.compiler.llvm.Function;
import org.robovm.compiler.llvm.FunctionDeclaration;
import org.robovm.compiler.llvm.FunctionRef;
import org.robovm.compiler.llvm.FunctionType;
import org.robovm.compiler.llvm.Global;
import org.robovm.compiler.llvm.IntegerConstant;
import org.robovm.compiler.llvm.NullConstant;
import org.robovm.compiler.llvm.Ret;
import org.robovm.compiler.llvm.StructureConstant;
import org.robovm.compiler.llvm.StructureConstantBuilder;
import org.robovm.compiler.llvm.Type;
import org.robovm.compiler.llvm.Unreachable;
import org.robovm.compiler.llvm.Value;
import org.robovm.compiler.plugin.CompilerPlugin;
import org.robovm.llvm.Context;
import org.robovm.llvm.Module;
import org.robovm.llvm.PassManager;
import org.robovm.llvm.Target;
import org.robovm.llvm.TargetMachine;
import org.robovm.llvm.binding.CodeGenFileType;
import org.robovm.llvm.binding.RelocMode;
/**
*
*/
public class Linker {
private static final TypeInfo[] EMPTY_TYPE_INFOS = new TypeInfo[0];
private static class TypeInfo implements Comparable {
boolean error;
Clazz clazz;
List children = new ArrayList();
int id;
/**
* Ordered list of TypeInfos for each superclass of this class and the
* class itself. Empty if this is an interface.
*/
TypeInfo[] classTypes;
/**
* Unordered list of TypeInfos for each interface implemented by this
* class or interface.
*/
TypeInfo[] interfaceTypes;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TypeInfo other = (TypeInfo) obj;
if (id != other.id)
return false;
return true;
}
@Override
public int compareTo(TypeInfo o) {
return id < o.id ? -1 : (id == o.id ? 0 : 1);
}
}
private final Config config;
private final Map runtimeData = new HashMap<>();
private final Map bcGlobalData = new HashMap<>();
public Linker(Config config) {
this.config = config;
}
/**
* Adds arbitrary data which will be compiled into the executable and will
* be available at runtime using {@code VM.getRuntimeData(id)}.
*/
public void addRuntimeData(String id, byte[] data) {
Objects.requireNonNull(id, "id");
Objects.requireNonNull(data, "data");
runtimeData.put(id, data);
}
/**
* adds global with data.
* allows plugins to register own data to be accesible in native/bc code
*/
public void addBcGlobalData(String name, byte[]data) {
Objects.requireNonNull(name, "name");
Objects.requireNonNull(data, "data");
bcGlobalData.put(name, data);
}
private ArrayConstant runtimeDataToBytes() throws UnsupportedEncodingException {
// The data consists of key value pairs prefixed with the number of
// pairs (int). In each pair the key is an UTF8 encoded string prefixed
// with the key's length (int) and the data is a byte array prefixed
// with the data length (int). We prefix the data with the length of the
// data (int).
LinkedList dataList = new LinkedList<>();
int length = 4;
for (Entry entry : runtimeData.entrySet()) {
dataList.add(entry.getKey().getBytes("UTF8"));
length += 4 + dataList.getLast().length;
dataList.add(entry.getValue());
length += 4 + dataList.getLast().length;
}
ByteBuffer bb = ByteBuffer.wrap(new byte[length + 4]).order(config.getArch().getByteOrder());
bb.putInt(length);
bb.putInt(runtimeData.size());
for (byte[] b : dataList) {
bb.putInt(b.length);
bb.put(b);
}
return new ArrayConstantBuilder(I8).add(bb.array()).build();
}
public void link(Set classes) throws IOException {
for (CompilerPlugin plugin : config.getCompilerPlugins()) {
plugin.beforeLinker(config, this, classes);
}
Arch arch = config.getArch();
OS os = config.getOs();
Set linkClasses = new TreeSet(classes);
config.getLogger().info("Linking %d classes (%s %s %s)", linkClasses.size(),
os, arch, config.isDebug() ? "debug" : "release");
ModuleBuilder mb = new ModuleBuilder();
mb.addInclude(getClass().getClassLoader().getResource(String.format("header-%s-%s.ll", os.getFamily(), arch.getCpuArch())));
mb.addInclude(getClass().getClassLoader().getResource("header.ll"));
mb.addGlobal(new Global("_bcRuntimeData", runtimeDataToBytes()));
// add globals from plugins
for (Entry e : bcGlobalData.entrySet()) {
ArrayConstant value = new ArrayConstantBuilder(I8).add(e.getValue()).build();
mb.addGlobal(new Global(e.getKey(), new ConstantGetelementptr(mb.newGlobal(value, true).ref(), 0, 0)));
}
ArrayConstantBuilder staticLibs = new ArrayConstantBuilder(I8_PTR);
for (Config.Lib lib : config.getLibs()) {
String p = lib.getValue();
if (p.endsWith(".a")) {
p = new File(p).getName();
String libName = p.substring(0, p.length() - 2);
if (libName.startsWith("lib")) {
libName = libName.substring(3);
}
staticLibs.add(mb.getString(libName));
}
}
staticLibs.add(new NullConstant(Type.I8_PTR));
mb.addGlobal(new Global("_bcStaticLibs",
new ConstantGetelementptr(mb.newGlobal(staticLibs.build()).ref(), 0, 0)));
HashTableGenerator bcpHashGen = new HashTableGenerator(
new ModifiedUtf8HashFunction());
HashTableGenerator cpHashGen = new HashTableGenerator(
new ModifiedUtf8HashFunction());
int classCount = 0;
Map typeInfos = new HashMap();
for (Clazz clazz : linkClasses) {
TypeInfo typeInfo = new TypeInfo();
typeInfo.clazz = clazz;
typeInfo.id = classCount++;
typeInfos.put(clazz.getClazzInfo(), typeInfo);
StructureConstant infoErrorStruct = createClassInfoErrorStruct(mb, clazz.getClazzInfo());
Global info = null;
if (infoErrorStruct == null) {
info = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), external, I8_PTR, false);
} else {
typeInfo.error = true;
info = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), infoErrorStruct);
}
mb.addGlobal(info);
if (clazz.isInBootClasspath()) {
bcpHashGen.put(clazz.getInternalName(), new ConstantBitcast(info.ref(), I8_PTR));
} else {
cpHashGen.put(clazz.getInternalName(), new ConstantBitcast(info.ref(), I8_PTR));
}
}
mb.addGlobal(new Global("_bcBootClassesHash", new ConstantGetelementptr(mb.newGlobal(bcpHashGen.generate(),
true).ref(), 0, 0)));
mb.addGlobal(new Global("_bcClassesHash", new ConstantGetelementptr(mb.newGlobal(cpHashGen.generate(), true)
.ref(), 0, 0)));
ArrayConstantBuilder bootClasspathValues = new ArrayConstantBuilder(I8_PTR);
ArrayConstantBuilder classpathValues = new ArrayConstantBuilder(I8_PTR);
List allPaths = new ArrayList();
allPaths.addAll(config.getClazzes().getPaths());
allPaths.addAll(config.getResourcesPaths());
for (Path path : allPaths) {
String entryName = null;
if (config.isSkipInstall() && config.getTarget().canLaunchInPlace()) {
entryName = path.getFile().getAbsolutePath();
} else {
entryName = config.getTarget().getInstallRelativeArchivePath(path);
}
if (path.isInBootClasspath()) {
bootClasspathValues.add(mb.getString(entryName));
} else {
classpathValues.add(mb.getString(entryName));
}
}
bootClasspathValues.add(new NullConstant(Type.I8_PTR));
classpathValues.add(new NullConstant(Type.I8_PTR));
mb.addGlobal(new Global("_bcBootclasspath", new ConstantGetelementptr(mb.newGlobal(bootClasspathValues.build())
.ref(), 0, 0)));
mb.addGlobal(new Global("_bcClasspath", new ConstantGetelementptr(mb.newGlobal(classpathValues.build()).ref(),
0, 0)));
if (config.getMainClass() != null) {
mb.addGlobal(new Global("_bcMainClass", mb.getString(config.getMainClass())));
}
ModuleBuilder[] mbs = new ModuleBuilder[config.getThreads() + 1];
FunctionRef[] stubRefs = new FunctionRef[mbs.length];
ArrayConstantBuilder stubRefsArray = new ArrayConstantBuilder(I8_PTR);
mbs[0] = mb;
for (int i = 1; i < mbs.length; i++) {
mbs[i] = new ModuleBuilder();
mbs[i].addInclude(getClass().getClassLoader().getResource(
String.format("header-%s-%s.ll", os.getFamily(), arch.getCpuArch())));
mbs[i].addInclude(getClass().getClassLoader().getResource("header.ll"));
Function fn = new FunctionBuilder("_stripped_method" + i, new FunctionType(VOID, ENV_PTR))
.linkage(external).build();
call(fn, BC_THROW_NO_SUCH_METHOD_ERROR, fn.getParameterRef(0),
mbs[i].getString("Method has been stripped out of the executable"));
fn.add(new Unreachable());
mbs[i].addFunction(fn);
mb.addFunctionDeclaration(new FunctionDeclaration(fn.ref()));
stubRefs[i] = fn.ref();
stubRefsArray.add(new ConstantBitcast(fn.ref(), I8_PTR));
}
stubRefsArray.add(new NullConstant(I8_PTR));
mb.addGlobal(new Global("_bcStrippedMethodStubs", stubRefsArray.build()));
Random rnd = new Random();
buildTypeInfos(typeInfos);
Set checkcasts = new HashSet<>();
Set instanceofs = new HashSet<>();
Set invokes = new HashSet<>();
for (Clazz clazz : linkClasses) {
ClazzInfo ci = clazz.getClazzInfo();
checkcasts.addAll(ci.getCheckcasts());
instanceofs.addAll(ci.getInstanceofs());
invokes.addAll(ci.getInvokes());
}
Set reachableMethods = new HashSet<>();
for (Triple node : config.getDependencyGraph().findReachableMethods()) {
reachableMethods.add(node.getLeft() + "." + node.getMiddle() + node.getRight());
}
int totalMethodCount = 0;
int reachableMethodCount = 0;
for (Clazz clazz : linkClasses) {
int mbIdx = rnd.nextInt(mbs.length - 1) + 1;
ClazzInfo ci = clazz.getClazzInfo();
// Create strong stubs for unused methods which override the weak
// ones generated by ClassCompiler. This must be done before we
// override lookup functions below otherwise we may get duplicate
// symbols errors.
for (MethodInfo mi : ci.getMethods()) {
if (!mi.isAbstract()) {
totalMethodCount++;
if (!reachableMethods.contains(clazz.getInternalName() + "." + mi.getName() + mi.getDesc())) {
createStrippedMethodStub(stubRefs[mbIdx], mbs[mbIdx], clazz, mi);
} else {
reachableMethodCount++;
}
}
}
TypeInfo typeInfo = typeInfos.get(ci);
if (typeInfo.error) {
// Add an empty TypeInfo
mb.addGlobal(new Global(Symbols.typeInfoSymbol(clazz.getInternalName()),
new StructureConstantBuilder()
.add(new IntegerConstant(typeInfo.id))
.add(new IntegerConstant(0))
.add(new IntegerConstant(-1))
.add(new IntegerConstant(0))
.add(new IntegerConstant(0))
.build()));
} else {
int[] classIds = new int[typeInfo.classTypes.length];
for (int i = 0; i < typeInfo.classTypes.length; i++) {
classIds[i] = typeInfo.classTypes[i].id;
}
int[] interfaceIds = new int[typeInfo.interfaceTypes.length];
for (int i = 0; i < typeInfo.interfaceTypes.length; i++) {
interfaceIds[i] = typeInfo.interfaceTypes[i].id;
}
mb.addGlobal(new Global(Symbols.typeInfoSymbol(clazz.getInternalName()),
new StructureConstantBuilder()
.add(new IntegerConstant(typeInfo.id))
.add(new IntegerConstant((typeInfo.classTypes.length - 1) * 4 + 5 * 4))
.add(new IntegerConstant(-1))
.add(new IntegerConstant(typeInfo.classTypes.length))
.add(new IntegerConstant(typeInfo.interfaceTypes.length))
.add(new ArrayConstantBuilder(I32).add(classIds).build())
.add(new ArrayConstantBuilder(I32).add(interfaceIds).build())
.build()));
if (!config.isDebug() && !ci.isInterface() && !ci.isFinal() && typeInfo.children.isEmpty()) {
// Non-final class with 0 children. Override every lookup
// function with one
// which doesn't do any lookup.
for (MethodInfo mi : ci.getMethods()) {
String name = mi.getName();
if (!name.equals("") && !name.equals("")
&& !mi.isPrivate() && !mi.isStatic() && !mi.isFinal() && !mi.isAbstract()) {
if (invokes.contains(clazz.getInternalName() + "." + name + mi.getDesc())) {
if (reachableMethods.contains(clazz.getInternalName() + "." + name + mi.getDesc())) {
mbs[mbIdx].addFunction(createLookup(mbs[mbIdx], ci, mi));
}
}
}
}
}
}
if (checkcasts.contains(clazz.getInternalName())) {
mbs[mbIdx].addFunction(createCheckcast(mbs[mbIdx], clazz, typeInfo));
}
if (instanceofs.contains(clazz.getInternalName())) {
mbs[mbIdx].addFunction(createInstanceof(mbs[mbIdx], clazz, typeInfo));
}
}
config.getLogger().info("%d methods out of %d included in the executable", reachableMethodCount, totalMethodCount);
List objectFiles = new ArrayList();
generateMachineCode(config, mbs, objectFiles);
for (Clazz clazz : linkClasses) {
objectFiles.add(config.getOFile(clazz));
}
/*
* Assemble the lines files for all linked classes into the module.
*/
for (Clazz clazz : linkClasses) {
File f = config.getLinesOFile(clazz);
if (f.exists() && f.length() > 0) {
objectFiles.add(f);
}
}
/*
* Assemble the debug info files for all linked classes into the module
*/
if (config.isDebug()) {
for (Clazz clazz : linkClasses) {
File f = config.getDebugInfoOFile(clazz);
if (f.exists() && f.length() > 0) {
objectFiles.add(f);
}
}
}
File binaryFile = config.getTarget().build(objectFiles);
for (CompilerPlugin plugin : config.getCompilerPlugins()) {
plugin.afterLinker(config, binaryFile);
}
}
private void generateMachineCode(final Config config, ModuleBuilder[] mbs,
final List objectFiles) throws IOException {
/*
* Make sure the tmpDir exists before we launch the worker threads. This
* is to prevent a race between threads to create this folder. See #631.
*/
config.getTmpDir().mkdirs();
Executor executor = config.getThreads() <= 1 ? AppCompiler.SAME_THREAD_EXECUTOR
: Executors.newFixedThreadPool(config.getThreads());
final List errors = Collections.synchronizedList(new ArrayList());
for (int i = 0; i < mbs.length; i++) {
final ModuleBuilder mb = mbs[i];
final int num = i;
executor.execute(new Runnable() {
public void run() {
try {
File linkerO = generateMachineCode(config, mb, num);
synchronized (objectFiles) {
objectFiles.add(linkerO);
}
} catch (Throwable t) {
errors.add(t);
}
}
});
}
// Shutdown the executor and wait for running tasks to complete.
if (executor instanceof ExecutorService) {
ExecutorService executorService = (ExecutorService) executor;
executorService.shutdown();
try {
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {
}
}
if (!errors.isEmpty()) {
Throwable t = errors.get(0);
if (t instanceof IOException) {
throw (IOException) t;
}
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
throw new CompilerException(t);
}
}
private File generateMachineCode(final Config config, final ModuleBuilder mb,
final int num) throws IOException {
File linkerO = new File(config.getTmpDir(), "linker" + num + ".o");
linkerO.getParentFile().mkdirs();
try (Context context = new Context()) {
// emit bitcode section for linker?.o
ClassCompiler.emitBitcodeSection(config, mb);
String ir = mb.build().toString();
if (config.isDumpIntermediates()) {
File linkerLl = new File(config.getTmpDir(), "linker" + num + ".ll");
FileUtils.writeStringToFile(linkerLl, ir, "utf-8");
}
try (Module module = Module.parseIR(context, ir, "linker" + num + ".ll")) {
try (PassManager passManager = new PassManager()) {
passManager.addAlwaysInlinerPass();
passManager.addPromoteMemoryToRegisterPass();
passManager.run(module);
}
if (config.isDumpIntermediates()) {
File linkerBc = new File(config.getTmpDir(), "linker" + num + ".bc");
module.writeBitcode(linkerBc);
}
String triple = config.getTriple();
Target target = Target.lookupTarget(triple);
try (TargetMachine targetMachine = target.createTargetMachine(
triple, null, null, null, RelocMode.RelocPIC, null)) {
targetMachine.setAsmVerbosityDefault(true);
targetMachine.setFunctionSections(true);
targetMachine.setDataSections(true);
targetMachine.getOptions().setNoFramePointerElim(true);
// NOTE: Doesn't have any effect on x86. See #503.
targetMachine.getOptions().setPositionIndependentExecutable(true);
if (config.isDumpIntermediates()) {
File linkerS = new File(config.getTmpDir(), "linker" + num + ".s");
try (OutputStream outS = new BufferedOutputStream(new FileOutputStream(linkerS))) {
targetMachine.emit(module, outS, CodeGenFileType.AssemblyFile);
}
}
try (OutputStream outO = new BufferedOutputStream(new FileOutputStream(linkerO))) {
targetMachine.emit(module, outO, CodeGenFileType.ObjectFile);
}
}
}
}
return linkerO;
}
private TypeInfo buildTypeInfo(TypeInfo typeInfo, Map typeInfos) {
if (typeInfo.error || typeInfo.classTypes != null) {
return typeInfo;
}
ClazzInfo ci = typeInfo.clazz.getClazzInfo();
List clTypeInfos = new ArrayList();
Set ifTypeInfos = new TreeSet();
if (!ci.isInterface()) {
if (ci.hasSuperclass()) {
TypeInfo superTypeInfo = buildTypeInfo(typeInfos.get(ci.getSuperclass()), typeInfos);
if (superTypeInfo.error) {
typeInfo.error = true;
return typeInfo;
}
clTypeInfos.addAll(Arrays.asList(superTypeInfo.classTypes));
clTypeInfos.add(typeInfo);
ifTypeInfos.addAll(Arrays.asList(superTypeInfo.interfaceTypes));
superTypeInfo.children.add(typeInfo.clazz);
} else {
clTypeInfos.add(typeInfo);
}
}
for (ClazzInfo ifCi : ci.getInterfaces()) {
TypeInfo ifTypeInfo = buildTypeInfo(typeInfos.get(ifCi), typeInfos);
if (ifTypeInfo.error) {
typeInfo.error = true;
return typeInfo;
}
ifTypeInfos.addAll(Arrays.asList(ifTypeInfo.interfaceTypes));
}
if (ci.isInterface()) {
ifTypeInfos.add(typeInfo);
}
typeInfo.classTypes = EMPTY_TYPE_INFOS;
typeInfo.interfaceTypes = EMPTY_TYPE_INFOS;
if (!clTypeInfos.isEmpty()) {
typeInfo.classTypes = clTypeInfos.toArray(new TypeInfo[clTypeInfos.size()]);
}
if (!ifTypeInfos.isEmpty()) {
typeInfo.interfaceTypes = ifTypeInfos.toArray(new TypeInfo[ifTypeInfos.size()]);
}
return typeInfo;
}
private void buildTypeInfos(Map typeInfos) {
for (TypeInfo typeInfo : typeInfos.values()) {
buildTypeInfo(typeInfo, typeInfos);
}
}
private StructureConstant createClassInfoErrorStruct(ModuleBuilder mb, ClazzInfo ci) {
/*
* Check that the class can be loaded, i.e. that the superclass and
* interfaces of the class exist and are accessible to the class. Also
* check that any exception the class uses in catch clauses exist and is
* accessible to the class. If the class cannot be loaded we override
* the ClassInfoHeader struct produced by the ClassCompiler for the
* class with one which tells the code in bc.c to throw an appropriate
* exception whenever the class is accessed.
*/
int errorType = ClassCompiler.CI_ERROR_TYPE_NONE;
String errorMessage = null;
if (!ci.isInterface() && ci.hasSuperclass()) {
// Check superclass
ClazzInfo superclazz = ci.getSuperclass();
if (superclazz.isPhantom()) {
errorType = ClassCompiler.CI_ERROR_TYPE_NO_CLASS_DEF_FOUND;
errorMessage = superclazz.getName();
} else if (!checkClassAccessible(superclazz, ci)) {
errorType = ClassCompiler.CI_ERROR_TYPE_ILLEGAL_ACCESS;
errorMessage = String.format(ILLEGAL_ACCESS_ERROR_CLASS, superclazz, ci);
} else if (superclazz.isInterface()) {
errorType = ClassCompiler.CI_ERROR_TYPE_INCOMPATIBLE_CLASS_CHANGE;
errorMessage = String.format("class %s has interface %s as super class", ci, superclazz);
}
// No need to check for ClassCircularityError. Soot doesn't handle
// such problems so the compilation will fail earlier.
}
if (errorType == ClassCompiler.CI_ERROR_TYPE_NONE) {
// Check interfaces
for (ClazzInfo interfaze : ci.getInterfaces()) {
if (interfaze.isPhantom()) {
errorType = ClassCompiler.CI_ERROR_TYPE_NO_CLASS_DEF_FOUND;
errorMessage = interfaze.getName();
break;
} else if (!checkClassAccessible(interfaze, ci)) {
errorType = ClassCompiler.CI_ERROR_TYPE_ILLEGAL_ACCESS;
errorMessage = String.format(ILLEGAL_ACCESS_ERROR_CLASS, interfaze, ci);
break;
} else if (!interfaze.isInterface()) {
errorType = ClassCompiler.CI_ERROR_TYPE_INCOMPATIBLE_CLASS_CHANGE;
errorMessage = String.format("class %s tries to implement class %s as interface",
ci, interfaze);
break;
}
}
}
if (errorType == ClassCompiler.CI_ERROR_TYPE_NONE) {
// Check exceptions used in catch clauses. I cannot find any info in
// the VM spec specifying that this has to be done when the class is
// loaded. However, this is how it's done in other VMs so we do it
// too.
for (ClazzInfo ex : ci.getCatches()) {
if (ex == null || ex.isPhantom()) {
errorType = ClassCompiler.CI_ERROR_TYPE_NO_CLASS_DEF_FOUND;
errorMessage = ex.getInternalName();
break;
} else if (!checkClassAccessible(ex, ci)) {
errorType = ClassCompiler.CI_ERROR_TYPE_ILLEGAL_ACCESS;
errorMessage = String.format(ILLEGAL_ACCESS_ERROR_CLASS, ex, ci);
break;
}
}
}
if (errorType == ClassCompiler.CI_ERROR_TYPE_NONE) {
return null;
}
// Create a ClassInfoError struct
StructureConstantBuilder error = new StructureConstantBuilder();
error.add(new NullConstant(I8_PTR)); // Points to the runtime Class
// struct
error.add(new IntegerConstant(ClassCompiler.CI_ERROR));
error.add(mb.getString(ci.getInternalName()));
error.add(new IntegerConstant(errorType));
error.add(mb.getString(errorMessage));
return error.build();
}
private void createStrippedMethodStub(FunctionRef stubRef, ModuleBuilder mb, Clazz clazz, MethodInfo mi) {
String symbol = methodSymbol(clazz.getInternalName(), mi.getName(), mi.getDesc());
Alias alias = new Alias(symbol, external, stubRef);
mb.addAlias(alias);
}
private Function createCheckcast(ModuleBuilder mb, Clazz clazz, TypeInfo typeInfo) {
Function fn = FunctionBuilder.checkcast(clazz);
Value info = getInfoStruct(mb, fn, clazz);
if (typeInfo.error) {
// This will trigger an exception
call(fn, BC_LDC_CLASS, fn.getParameterRef(0), info);
fn.add(new Ret(new NullConstant(Types.OBJECT_PTR)));
} else if (!clazz.getClazzInfo().isInterface()) {
Value result = call(fn, CHECKCAST_CLASS, fn.getParameterRef(0), info,
fn.getParameterRef(1),
new IntegerConstant((typeInfo.classTypes.length - 1) * 4 + 5 * 4),
new IntegerConstant(typeInfo.id));
fn.add(new Ret(result));
} else {
Value result = call(fn, CHECKCAST_INTERFACE, fn.getParameterRef(0), info,
fn.getParameterRef(1),
new IntegerConstant(typeInfo.id));
fn.add(new Ret(result));
}
return fn;
}
private Function createInstanceof(ModuleBuilder mb, Clazz clazz, TypeInfo typeInfo) {
Function fn = FunctionBuilder.instanceOf(clazz);
Value info = getInfoStruct(mb, fn, clazz);
if (typeInfo.error) {
// This will trigger an exception
call(fn, BC_LDC_CLASS, fn.getParameterRef(0), info);
fn.add(new Ret(new IntegerConstant(0)));
} else if (!clazz.getClazzInfo().isInterface()) {
Value result = call(fn, INSTANCEOF_CLASS, fn.getParameterRef(0), info,
fn.getParameterRef(1),
new IntegerConstant((typeInfo.classTypes.length - 1) * 4 + 5 * 4),
new IntegerConstant(typeInfo.id));
fn.add(new Ret(result));
} else {
Value result = call(fn, INSTANCEOF_INTERFACE, fn.getParameterRef(0), info,
fn.getParameterRef(1),
new IntegerConstant(typeInfo.id));
fn.add(new Ret(result));
}
return fn;
}
private Function createLookup(ModuleBuilder mb, ClazzInfo ci, MethodInfo mi) {
Function function = FunctionBuilder.lookup(ci, mi, false);
String targetFnName = mi.isSynchronized()
? Symbols.synchronizedWrapperSymbol(ci.getInternalName(), mi.getName(), mi.getDesc())
: Symbols.methodSymbol(ci.getInternalName(), mi.getName(), mi.getDesc());
FunctionRef fn = new FunctionRef(targetFnName, function.getType());
if (!mb.hasSymbol(fn.getName())) {
mb.addFunctionDeclaration(new FunctionDeclaration(fn));
}
Value result = tailcall(function, fn, function.getParameterRefs());
function.add(new Ret(result));
return function;
}
private Value getInfoStruct(ModuleBuilder mb, Function f, Clazz clazz) {
String symbol = Symbols.infoStructSymbol(clazz.getInternalName());
if (!mb.hasSymbol(symbol)) {
Global info = new Global(symbol, external, I8_PTR, false);
mb.addGlobal(info);
return info.ref();
} else {
return new ConstantBitcast(mb.getGlobalRef(symbol), I8_PTR_PTR);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy