All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sandius.rembulan.compiler.gen.asm.ASMBytecodeEmitter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016 Miroslav Janíček
 *
 * 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 net.sandius.rembulan.compiler.gen.asm;

import net.sandius.rembulan.Variable;
import net.sandius.rembulan.compiler.CompilerSettings;
import net.sandius.rembulan.compiler.FunctionId;
import net.sandius.rembulan.compiler.IRFunc;
import net.sandius.rembulan.compiler.analysis.DependencyInfo;
import net.sandius.rembulan.compiler.analysis.SlotAllocInfo;
import net.sandius.rembulan.compiler.analysis.TypeInfo;
import net.sandius.rembulan.compiler.gen.BytecodeEmitter;
import net.sandius.rembulan.compiler.gen.ClassNameTranslator;
import net.sandius.rembulan.compiler.gen.CompiledClass;
import net.sandius.rembulan.compiler.gen.asm.helpers.ASMUtils;
import net.sandius.rembulan.compiler.gen.asm.helpers.InvokableMethods;
import net.sandius.rembulan.compiler.gen.asm.helpers.InvokeKind;
import net.sandius.rembulan.compiler.ir.AbstractVar;
import net.sandius.rembulan.compiler.ir.UpVar;
import net.sandius.rembulan.compiler.ir.Var;
import net.sandius.rembulan.impl.DefaultSavedState;
import net.sandius.rembulan.util.ByteVector;
import net.sandius.rembulan.util.Check;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.util.CheckClassAdapter;
import org.objectweb.asm.util.TraceClassVisitor;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.V1_7;

public class ASMBytecodeEmitter extends BytecodeEmitter {

	public final IRFunc fn;
	public final SlotAllocInfo slots;
	public final TypeInfo types;
	public final DependencyInfo deps;

	public final CompilerSettings compilerSettings;
	public final ClassNameTranslator classNameTranslator;

	private final String sourceFile;

	private final ClassNode classNode;

	private final HashMap upvalueFieldNames;

	private final List fields;

	private boolean verifyAndPrint;

	public ASMBytecodeEmitter(
			IRFunc fn,
			SlotAllocInfo slots,
			TypeInfo types,
			DependencyInfo deps,
			CompilerSettings compilerSettings,
			ClassNameTranslator classNameTranslator,
			String sourceFile) {

		this.fn = Check.notNull(fn);
		this.slots = Check.notNull(slots);
		this.types = Check.notNull(types);
		this.deps = Check.notNull(deps);

		this.compilerSettings = Check.notNull(compilerSettings);
		this.classNameTranslator = Check.notNull(classNameTranslator);
		this.sourceFile = Check.notNull(sourceFile);

		classNode = new ClassNode();

		this.fields = new ArrayList<>();

		upvalueFieldNames = new HashMap<>();

		String s = System.getProperty("net.sandius.rembulan.compiler.VerifyAndPrint");
		verifyAndPrint = s != null && "true".equals(s.trim().toLowerCase());
	}

	int kind() {
		return InvokeKind.adjust_nativeKind(InvokeKind.encode(fn.params().size(), fn.isVararg()));
	}

	String thisClassName() {
		return fn.id().toClassName(classNameTranslator);
	}

	Type thisClassType() {
		return ASMUtils.typeForClassName(thisClassName());
	}

	Type superClassType() {
		return Type.getType(InvokeKind.nativeClassForKind(kind()));
	}

	Type parentClassType() {
		FunctionId parentId = fn.id().parent();
		return parentId != null
				? ASMUtils.typeForClassName(parentId.toClassName(classNameTranslator))
				: null;
	}

	public Type savedStateClassType() {
		return Type.getType(DefaultSavedState.class);
	}

	Type invokeMethodType() {
		return InvokableMethods.invoke_method(kind()).getMethodType();
	}

	public boolean hasUpvalues() {
		return !fn.upvals().isEmpty();
	}

	public int numOfParameters() {
		return fn.params().size();
	}

	public boolean isVararg() {
		return fn.isVararg();
	}

	public List fields() {
		return fields;
	}

	private void addInnerClassLinks() {
		String ownInternalName = thisClassType().getInternalName();

		// parent
		if (parentClassType() != null) {
			String parentInternalName = parentClassType().getInternalName();

			// assume (parentInternalName + "$") is the prefix of ownInternalName
			String suffix = ownInternalName.substring(parentInternalName.length() + 1);

			classNode.innerClasses.add(new InnerClassNode(
					ownInternalName,
					parentInternalName,
					suffix,
					ACC_PUBLIC + ACC_STATIC));
		}

		List nestedIds = new ArrayList<>(deps.nestedRefs());
		Collections.sort(nestedIds, FunctionId.LEXICOGRAPHIC_COMPARATOR);

		for (FunctionId childId : nestedIds) {
			String childClassName = childId.toClassName(classNameTranslator);
			String childInternalName = ASMUtils.typeForClassName(childClassName).getInternalName();

			// assume (ownInternalName + "$") is the prefix of childName
			String suffix = childInternalName.substring(ownInternalName.length() + 1);

			classNode.innerClasses.add(new InnerClassNode(
					childInternalName,
					ownInternalName,
					suffix,
					ACC_PUBLIC + ACC_STATIC));
		}
	}

	enum NestedInstanceKind {
		Pure,
		Closed,
		Open
	}

	protected static NestedInstanceKind functionKind(IRFunc fn) {
		if (fn.upvals().isEmpty()) {
			return NestedInstanceKind.Pure;
		}
		else {
			for (AbstractVar uv : fn.upvals()) {
				if (uv instanceof Var) {
					return NestedInstanceKind.Open;
				}
			}
			return NestedInstanceKind.Closed;
		}
	}

	public static String instanceFieldName() {
		return "INSTANCE";
	}

	private FieldNode instanceField() {
		return new FieldNode(
				ACC_PUBLIC + ACC_FINAL + ACC_STATIC,
				instanceFieldName(),
				thisClassType().getDescriptor(),
				null,
				null);
	}

	String addFieldName(String n) {
		// TODO
		return n;
	}

	private static String toFieldName(String n) {
		return n;  // TODO
	}

	private static String ensureUnique(Collection ss, String s) {
		int idx = 0;
		String prefix = s;

		while (ss.contains(s)) {
			s = prefix + "_" + (idx++);
		}

		return s;
	}

	private static String preferredUpvalueName(UpVar uv) {
		return uv.name().value();
	}

	private void addUpvalueFields() {
		for (UpVar uv : fn.upvals()) {
			String name = toFieldName(ensureUnique(upvalueFieldNames.values(), preferredUpvalueName(uv)));
			upvalueFieldNames.put(uv, name);

			FieldNode fieldNode = new FieldNode(
					ACC_PROTECTED + ACC_FINAL,
					name,
					Type.getDescriptor(Variable.class),
					null,
					null);

			classNode.fields.add(fieldNode);
		}
	}

	public String getUpvalueFieldName(UpVar uv) {
		String name = upvalueFieldNames.get(uv);
		if (name == null) {
			throw new IllegalArgumentException("upvalue field name is null for upvalue " + uv);
		}
		return name;
	}

	public ClassNode classNode() {
		classNode.version = V1_7;
		classNode.access = ACC_PUBLIC + ACC_SUPER;
		classNode.name = thisClassType().getInternalName();
		classNode.superName = superClassType().getInternalName();
		classNode.sourceFile = sourceFile;

		addInnerClassLinks();

		if (!hasUpvalues()) {
			classNode.fields.add(instanceField());
		}

		addUpvalueFields();

		RunMethod runMethod = new RunMethod(this);

		for (RunMethod.ConstFieldInstance cfi : runMethod.constFields()) {
			classNode.fields.add(cfi.fieldNode());
		}

		ConstructorMethod ctor = new ConstructorMethod(this, runMethod);

		classNode.methods.add(ctor.methodNode());
		classNode.methods.add(new InvokeMethod(this, runMethod).methodNode());
		classNode.methods.add(new ResumeMethod(this, runMethod).methodNode());
		classNode.methods.addAll(runMethod.methodNodes());

		if (runMethod.usesSnapshotMethod()) {
			classNode.methods.add(runMethod.snapshotMethodNode());
		}

		StaticConstructorMethod staticCtor = new StaticConstructorMethod(this, ctor, runMethod);
		if (!staticCtor.isEmpty()) {
			classNode.methods.add(staticCtor.methodNode());
		}

		classNode.fields.addAll(fields);

		return classNode;
	}

	private byte[] classNodeToBytes(ClassNode classNode) {
		ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
		classNode.accept(writer);
		byte[] bytes = writer.toByteArray();

		// verify bytecode

		if (verifyAndPrint) {
			ClassReader reader = new ClassReader(bytes);
			ClassVisitor tracer = new TraceClassVisitor(new PrintWriter(System.out));
			ClassVisitor checker = new CheckClassAdapter(tracer, true);
			reader.accept(checker, 0);
		}

		return bytes;
	}

	@Override
	public CompiledClass emit() {
		ClassNode classNode = classNode();
		byte[] bytes = classNodeToBytes(classNode);
		return new CompiledClass(thisClassName(), ByteVector.wrap(bytes));
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy