use of org.robovm.compiler.plugin.CompilerPlugin in project robovm by robovm.
the class Linker method link.
public void link(Set<Clazz> classes) throws IOException {
for (CompilerPlugin plugin : config.getCompilerPlugins()) {
plugin.beforeLinker(config, this, classes);
}
Arch arch = config.getArch();
OS os = config.getOs();
Set<Clazz> linkClasses = new TreeSet<Clazz>(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)));
mb.addInclude(getClass().getClassLoader().getResource("header.ll"));
mb.addGlobal(new Global("_bcRuntimeData", runtimeDataToBytes()));
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<String, Constant> bcpHashGen = new HashTableGenerator<String, Constant>(new ModifiedUtf8HashFunction());
HashTableGenerator<String, Constant> cpHashGen = new HashTableGenerator<String, Constant>(new ModifiedUtf8HashFunction());
int classCount = 0;
Map<ClazzInfo, TypeInfo> typeInfos = new HashMap<ClazzInfo, TypeInfo>();
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<Path> allPaths = new ArrayList<Path>();
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)));
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<String> checkcasts = new HashSet<>();
Set<String> instanceofs = new HashSet<>();
Set<String> invokes = new HashSet<>();
for (Clazz clazz : linkClasses) {
ClazzInfo ci = clazz.getClazzInfo();
checkcasts.addAll(ci.getCheckcasts());
instanceofs.addAll(ci.getInstanceofs());
invokes.addAll(ci.getInvokes());
}
Set<String> reachableMethods = new HashSet<>();
for (Triple<String, String, String> 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();
// 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()) {
// which doesn't do any lookup.
for (MethodInfo mi : ci.getMethods()) {
String name = mi.getName();
if (!name.equals("<clinit>") && !name.equals("<init>") && !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<File> objectFiles = new ArrayList<File>();
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);
}
}
config.getTarget().build(objectFiles);
}
use of org.robovm.compiler.plugin.CompilerPlugin in project robovm by robovm.
the class ClassCompiler method compile.
private void compile(Clazz clazz, OutputStream out) throws IOException {
javaMethodCompiler.reset(clazz);
bridgeMethodCompiler.reset(clazz);
callbackMethodCompiler.reset(clazz);
nativeMethodCompiler.reset(clazz);
structMemberMethodCompiler.reset(clazz);
globalValueMethodCompiler.reset(clazz);
ClazzInfo ci = clazz.resetClazzInfo();
mb = new ModuleBuilder();
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
compilerPlugin.beforeClass(config, clazz, mb);
}
sootClass = clazz.getSootClass();
trampolines = new HashMap<>();
catches = new HashSet<String>();
classFields = getClassFields(config.getOs(), config.getArch(), sootClass);
instanceFields = getInstanceFields(config.getOs(), config.getArch(), sootClass);
classType = getClassType(config.getOs(), config.getArch(), sootClass);
instanceType = getInstanceType(config.getOs(), config.getArch(), sootClass);
attributesEncoder.encode(mb, sootClass);
// will never be initialized.
if (!sootClass.declaresMethodByName("<clinit>") && hasConstantValueTags(classFields)) {
SootMethod clinit = new SootMethod("<clinit>", Collections.EMPTY_LIST, VoidType.v(), Modifier.STATIC);
JimpleBody body = Jimple.v().newBody(clinit);
clinit.setActiveBody(body);
body.getUnits().add(new JReturnVoidStmt());
this.sootClass.addMethod(clinit);
}
if (isStruct(sootClass)) {
SootMethod _sizeOf = new SootMethod("_sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PROTECTED | Modifier.NATIVE);
sootClass.addMethod(_sizeOf);
SootMethod sizeOf = new SootMethod("sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PUBLIC | Modifier.STATIC | Modifier.NATIVE);
sootClass.addMethod(sizeOf);
}
mb.addInclude(getClass().getClassLoader().getResource(String.format("header-%s-%s.ll", config.getOs().getFamily(), config.getArch())));
mb.addInclude(getClass().getClassLoader().getResource("header.ll"));
mb.addFunction(createLdcClass());
mb.addFunction(createLdcClassWrapper());
Function allocator = createAllocator();
mb.addFunction(allocator);
mb.addFunction(createClassInitWrapperFunction(allocator.ref()));
for (SootField f : sootClass.getFields()) {
Function getter = createFieldGetter(f, classFields, classType, instanceFields, instanceType);
Function setter = createFieldSetter(f, classFields, classType, instanceFields, instanceType);
mb.addFunction(getter);
mb.addFunction(setter);
if (f.isStatic() && !f.isPrivate()) {
mb.addFunction(createClassInitWrapperFunction(getter.ref()));
if (!f.isFinal()) {
mb.addFunction(createClassInitWrapperFunction(setter.ref()));
}
}
}
// After this point no changes to methods/fields may be done by CompilerPlugins.
ci.initClassInfo();
for (SootMethod method : sootClass.getMethods()) {
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
compilerPlugin.beforeMethod(config, clazz, method, mb);
}
String name = method.getName();
Function function = null;
if (hasBridgeAnnotation(method)) {
function = bridgeMethod(method);
} else if (hasGlobalValueAnnotation(method)) {
function = globalValueMethod(method);
} else if (isStruct(sootClass) && ("_sizeOf".equals(name) || "sizeOf".equals(name) || hasStructMemberAnnotation(method))) {
function = structMember(method);
} else if (method.isNative()) {
function = nativeMethod(method);
} else if (!method.isAbstract()) {
function = method(method);
}
if (hasCallbackAnnotation(method)) {
callbackMethod(method);
}
if (!name.equals("<clinit>") && !name.equals("<init>") && !method.isPrivate() && !method.isStatic() && !Modifier.isFinal(method.getModifiers()) && !Modifier.isFinal(sootClass.getModifiers())) {
createLookupFunction(method);
}
if (method.isStatic() && !name.equals("<clinit>")) {
String fnName = method.isSynchronized() ? Symbols.synchronizedWrapperSymbol(method) : Symbols.methodSymbol(method);
FunctionRef fn = new FunctionRef(fnName, getFunctionType(method));
mb.addFunction(createClassInitWrapperFunction(fn));
}
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
if (function != null) {
compilerPlugin.afterMethod(config, clazz, method, mb, function);
}
}
}
for (Trampoline trampoline : trampolines.keySet()) {
Set<String> deps = new HashSet<String>();
Set<Triple<String, String, String>> mDeps = new HashSet<>();
trampolineResolver.compile(mb, clazz, trampoline, deps, mDeps);
for (SootMethod m : trampolines.get(trampoline)) {
MethodInfo mi = ci.getMethod(m.getName(), getDescriptor(m));
mi.addClassDependencies(deps, false);
mi.addInvokeMethodDependencies(mDeps, false);
}
}
/*
* Add method dependencies from overriding methods to the overridden
* super method(s). These will be reversed by the DependencyGraph to
* create edges from the super/interface method to the overriding
* method.
*/
Map<SootMethod, Set<SootMethod>> overriddenMethods = getOverriddenMethods(this.sootClass);
for (SootMethod from : overriddenMethods.keySet()) {
MethodInfo mi = ci.getMethod(from.getName(), getDescriptor(from));
for (SootMethod to : overriddenMethods.get(from)) {
mi.addSuperMethodDependency(getInternalName(to.getDeclaringClass()), to.getName(), getDescriptor(to), false);
}
}
/*
* Edge case. A method in a superclass might satisfy an interface method
* in the interfaces implemented by this class. See e.g. the abstract
* class HashMap$HashIterator which doesn't implement Iterator but has
* the hasNext() and other methods. We add a dependency from the current
* class to the super method to ensure it's included if the current
* class is linked in.
*/
if (sootClass.hasSuperclass()) {
for (SootClass interfaze : getImmediateInterfaces(sootClass)) {
for (SootMethod m : interfaze.getMethods()) {
if (!m.isStatic()) {
try {
this.sootClass.getMethod(m.getName(), m.getParameterTypes());
} catch (RuntimeException e) {
/*
* Not found. Find the implementation in
* superclasses.
*/
SootMethod superMethod = null;
for (SootClass sc = sootClass.getSuperclass(); sc.hasSuperclass(); sc = sc.getSuperclass()) {
try {
SootMethod candidate = sc.getMethod(m.getName(), m.getParameterTypes());
if (!candidate.isStatic()) {
superMethod = candidate;
break;
}
} catch (RuntimeException e2) {
// Not found.
}
}
if (superMethod != null) {
ci.addSuperMethodDependency(getInternalName(superMethod.getDeclaringClass()), superMethod.getName(), getDescriptor(superMethod), false);
}
}
}
}
}
}
Global classInfoStruct = null;
try {
if (!sootClass.isInterface()) {
config.getVTableCache().get(sootClass);
}
classInfoStruct = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), Linkage.weak, createClassInfoStruct());
} catch (IllegalArgumentException e) {
// VTable throws this if any of the superclasses of the class is actually an interface.
// Shouldn't happen frequently but the DRLVM test suite has some tests for this.
// The Linker will take care of making sure the class cannot be loaded at runtime.
classInfoStruct = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), I8_PTR, true);
}
mb.addGlobal(classInfoStruct);
/*
* Emit an internal i8* alias for the info struct which MethodCompiler
* can use when referencing this info struct in exception landing pads
* in methods in the same class. See #1007.
*/
mb.addAlias(new Alias(classInfoStruct.getName() + "_i8ptr", Linkage._private, new ConstantBitcast(classInfoStruct.ref(), I8_PTR)));
Function infoFn = FunctionBuilder.infoStruct(sootClass);
infoFn.add(new Ret(new ConstantBitcast(classInfoStruct.ref(), I8_PTR_PTR)));
mb.addFunction(infoFn);
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
compilerPlugin.afterClass(config, clazz, mb);
}
OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
mb.build().write(writer);
writer.flush();
ci.setCatchNames(catches);
// Make sure no class or interface has zero dependencies
ci.addClassDependency("java/lang/Object", false);
if (sootClass.hasSuperclass() && !sootClass.isInterface()) {
ci.addClassDependency(getInternalName(sootClass.getSuperclass()), false);
}
for (SootClass iface : sootClass.getInterfaces()) {
ci.addClassDependency(getInternalName(iface), false);
}
for (SootField f : sootClass.getFields()) {
addClassDependencyIfNeeded(clazz, f.getType(), false);
}
for (SootMethod m : sootClass.getMethods()) {
MethodInfo mi = ci.getMethod(m.getName(), getDescriptor(m));
addClassDependencyIfNeeded(clazz, mi, m.getReturnType(), false);
@SuppressWarnings("unchecked") List<soot.Type> paramTypes = (List<soot.Type>) m.getParameterTypes();
for (soot.Type type : paramTypes) {
addClassDependencyIfNeeded(clazz, mi, type, false);
}
}
ci.addClassDependencies(attributesEncoder.getDependencies(), false);
ci.addClassDependencies(catches, false);
for (Trampoline t : trampolines.keySet()) {
if (t instanceof Checkcast) {
ci.addCheckcast(t.getTarget());
} else if (t instanceof Instanceof) {
ci.addInstanceof(t.getTarget());
} else if (t instanceof Invokevirtual || t instanceof Invokeinterface) {
ci.addInvoke(t.getTarget() + "." + ((Invoke) t).getMethodName() + ((Invoke) t).getMethodDesc());
}
}
clazz.saveClazzInfo();
}
use of org.robovm.compiler.plugin.CompilerPlugin in project robovm by robovm.
the class ClassCompiler method generateMachineCode.
private static void generateMachineCode(Config config, Clazz clazz, byte[] llData, List<String> cCode) throws IOException {
if (config.isDumpIntermediates()) {
File llFile = config.getLlFile(clazz);
llFile.getParentFile().mkdirs();
FileUtils.writeByteArrayToFile(llFile, llData);
File cFile = config.getCFile(clazz);
if (cCode.isEmpty()) {
cFile.delete();
} else {
FileUtils.writeLines(cFile, "ascii", cCode);
}
}
File oFile = config.getOFile(clazz);
try (Context context = new Context()) {
try (Module module = Module.parseIR(context, llData, clazz.getClassName())) {
if (!cCode.isEmpty()) {
int size = 0;
for (String s : cCode) {
size += s.length();
}
StringBuilder sb = new StringBuilder(size);
for (String s : cCode) {
sb.append(s);
}
try (Module m2 = Module.parseClangString(context, sb.toString(), clazz.getClassName() + ".c", config.getClangTriple())) {
module.link(m2);
for (org.robovm.llvm.Function f1 : m2.getFunctions()) {
String name = f1.getName();
org.robovm.llvm.Function f2 = module.getFunctionByName(name);
if (Symbols.isBridgeCSymbol(name) || Symbols.isCallbackCSymbol(name) || Symbols.isCallbackInnerCSymbol(name)) {
f2.setLinkage(org.robovm.llvm.binding.Linkage.PrivateLinkage);
if (Symbols.isCallbackInnerCSymbol(name)) {
// TODO: We should also always inline the bridge functions but for some reason
// that makes the RoboVM tests hang indefinitely.
f2.removeAttribute(Attribute.NoInlineAttribute);
f2.addAttribute(Attribute.AlwaysInlineAttribute);
}
}
}
}
}
try (PassManager passManager = createPassManager(config)) {
passManager.run(module);
}
if (config.isDumpIntermediates()) {
File bcFile = config.getBcFile(clazz);
bcFile.getParentFile().mkdirs();
module.writeBitcode(bcFile);
}
String triple = config.getTriple();
Target target = Target.lookupTarget(triple);
try (TargetMachine targetMachine = target.createTargetMachine(triple, config.getArch().getLlvmCpu(), null, config.isDebug() ? CodeGenOptLevel.CodeGenLevelNone : 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(!config.isDebug());
ByteArrayOutputStream output = new ByteArrayOutputStream(256 * 1024);
targetMachine.emit(module, output, CodeGenFileType.AssemblyFile);
byte[] asm = output.toByteArray();
output.reset();
patchAsmWithFunctionSizes(config, clazz, new ByteArrayInputStream(asm), output);
asm = output.toByteArray();
if (config.isDumpIntermediates()) {
File sFile = config.getSFile(clazz);
sFile.getParentFile().mkdirs();
FileUtils.writeByteArrayToFile(sFile, asm);
}
oFile.getParentFile().mkdirs();
ByteArrayOutputStream oFileBytes = new ByteArrayOutputStream();
targetMachine.assemble(asm, clazz.getClassName(), oFileBytes);
new HfsCompressor().compress(oFile, oFileBytes.toByteArray(), config);
for (CompilerPlugin plugin : config.getCompilerPlugins()) {
plugin.afterObjectFile(config, clazz, oFile);
}
/*
* Read out line number info from the .o file if any and
* assemble into a separate .o file.
*/
String symbolPrefix = config.getOs().getFamily() == OS.Family.darwin ? "_" : "";
symbolPrefix += Symbols.EXTERNAL_SYMBOL_PREFIX;
ModuleBuilder linesMb = null;
try (ObjectFile objectFile = ObjectFile.load(oFile)) {
for (Symbol symbol : objectFile.getSymbols()) {
if (symbol.getSize() > 0 && symbol.getName().startsWith(symbolPrefix)) {
List<LineInfo> lineInfos = objectFile.getLineInfos(symbol);
if (!lineInfos.isEmpty()) {
Collections.sort(lineInfos, new Comparator<LineInfo>() {
public int compare(LineInfo o1, LineInfo o2) {
return Long.compare(o1.getAddress(), o2.getAddress());
}
});
// The base address of the method which will be used to calculate offsets into the method
long baseAddress = symbol.getAddress();
// The first line number in the method. All other line numbers in the table will be deltas against this.
int firstLineNumber = lineInfos.get(0).getLineNumber();
// Calculate the max address and line number offsets
long maxAddressOffset = 0;
long maxLineOffset = 0;
for (LineInfo lineInfo : lineInfos) {
maxAddressOffset = Math.max(maxAddressOffset, lineInfo.getAddress() - baseAddress);
maxLineOffset = Math.max(maxLineOffset, lineInfo.getLineNumber() - firstLineNumber);
}
// Calculate the number of bytes needed to represent the highest offsets.
// Either 1, 2 or 4 bytes will be used.
int addressOffsetSize = (maxAddressOffset & ~0xff) == 0 ? 1 : ((maxAddressOffset & ~0xffff) == 0 ? 2 : 4);
int lineOffsetSize = (maxLineOffset & ~0xff) == 0 ? 1 : ((maxLineOffset & ~0xffff) == 0 ? 2 : 4);
// The size of the address offsets table. We skip the first LineInfo as its offset is always 0.
int addressOffsetTableSize = addressOffsetSize * (lineInfos.size() - 1);
// Pad size of address offset table to make sure line offsets are aligned properly
int addressOffsetPadding = (lineOffsetSize - (addressOffsetTableSize & (lineOffsetSize - 1))) & (lineOffsetSize - 1);
addressOffsetTableSize += addressOffsetPadding;
// The first 32 bits of the line number info contains the number of line numbers
// minus the first. The 4 most significant bits are used to store the number of
// bytes needed by each entry in each table.
int flags = 0;
flags = addressOffsetSize - 1;
flags <<= 2;
flags |= lineOffsetSize - 1;
flags <<= 28;
flags |= (lineInfos.size() - 1) & 0x0fffffff;
StructureConstantBuilder builder = new StructureConstantBuilder();
builder.add(new IntegerConstant(flags)).add(new IntegerConstant(firstLineNumber));
for (LineInfo lineInfo : lineInfos.subList(1, lineInfos.size())) {
if (addressOffsetSize == 1) {
builder.add(new IntegerConstant((byte) (lineInfo.getAddress() - baseAddress)));
} else if (addressOffsetSize == 2) {
builder.add(new IntegerConstant((short) (lineInfo.getAddress() - baseAddress)));
} else {
builder.add(new IntegerConstant((int) (lineInfo.getAddress() - baseAddress)));
}
}
// Padding
for (int i = 0; i < addressOffsetPadding; i++) {
builder.add(new IntegerConstant((byte) 0));
}
for (LineInfo lineInfo : lineInfos.subList(1, lineInfos.size())) {
if (lineOffsetSize == 1) {
builder.add(new IntegerConstant((byte) (lineInfo.getLineNumber() - firstLineNumber)));
} else if (lineOffsetSize == 2) {
builder.add(new IntegerConstant((short) (lineInfo.getLineNumber() - firstLineNumber)));
} else {
builder.add(new IntegerConstant((int) (lineInfo.getLineNumber() - firstLineNumber)));
}
}
// Extract the method's name and descriptor from the symbol and
// build the linetable symbol name.
String methodName = symbol.getName().substring(symbol.getName().lastIndexOf('.') + 1);
methodName = methodName.substring(0, methodName.indexOf('('));
String methodDesc = symbol.getName().substring(symbol.getName().lastIndexOf('('));
String linetableSymbol = Symbols.linetableSymbol(clazz.getInternalName(), methodName, methodDesc);
if (linesMb == null) {
linesMb = new ModuleBuilder();
}
linesMb.addGlobal(new Global(linetableSymbol, builder.build(), true));
}
}
}
}
if (linesMb != null) {
byte[] linesData = linesMb.build().toString().getBytes("UTF-8");
if (config.isDumpIntermediates()) {
File linesLlFile = config.getLinesLlFile(clazz);
linesLlFile.getParentFile().mkdirs();
FileUtils.writeByteArrayToFile(linesLlFile, linesData);
}
try (Module linesModule = Module.parseIR(context, linesData, clazz.getClassName() + ".lines")) {
File linesOFile = config.getLinesOFile(clazz);
ByteArrayOutputStream linesOBytes = new ByteArrayOutputStream();
targetMachine.emit(linesModule, linesOBytes, CodeGenFileType.ObjectFile);
new HfsCompressor().compress(linesOFile, linesOBytes.toByteArray(), config);
}
} else {
// Make sure there's no stale lines.o file lingering
File linesOFile = config.getLinesOFile(clazz);
if (linesOFile.exists()) {
linesOFile.delete();
}
}
}
}
} catch (Throwable t) {
if (oFile.exists()) {
oFile.delete();
}
if (t instanceof IOException) {
throw (IOException) t;
}
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
if (t instanceof Error) {
throw (Error) t;
}
throw new CompilerException(t);
}
}
Aggregations