// Copyright 2015 The Bazel Authors. All rights reserved. // // 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 com.google.devtools.build.lib.syntax.compiler; import net.bytebuddy.description.method.MethodDescription.ForLoadedConstructor; import net.bytebuddy.implementation.Implementation.Context; import net.bytebuddy.implementation.bytecode.Duplication; import net.bytebuddy.implementation.bytecode.StackManipulation; import net.bytebuddy.implementation.bytecode.member.MethodInvocation; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.util.List; /** * Builds byte code for new object creation from constructor calls. * *

Java byte code for new object creation looks like the following pseudo code: *

 * NEW
 * DUP
 * ...load constructor parameters...
 * INVOKESPECIAL constructor
 * 
* This is because a constructor is actually called on the reference to the object itself, which * is left on the stack by NEW. * * This class helps with wrapping the parameter loading with this structure. */ public class NewObject implements StackManipulation { private final ForLoadedConstructor constructor; private final StackManipulation arguments; /** * Intermediate state builder for new object construction with missing constructor argument * loading. */ public static final class NewObjectBuilder { private final ForLoadedConstructor constructor; private NewObjectBuilder(ForLoadedConstructor constructor) { this.constructor = constructor; } /** * Adds the argument loading in the correct for new object construction. */ public NewObject arguments(StackManipulation... arguments) { return new NewObject(constructor, new StackManipulation.Compound(arguments)); } /** * Adds the argument loading in the correct for new object construction. */ public NewObject arguments(List arguments) { return new NewObject(constructor, new StackManipulation.Compound(arguments)); } } private NewObject(ForLoadedConstructor constructor, StackManipulation arguments) { this.constructor = constructor; this.arguments = arguments; } /** * Looks for a constructor in the class with the given parameter types and returns an * intermediate builder. */ public static NewObjectBuilder fromConstructor(Class clazz, Class... parameterTypes) { return new NewObjectBuilder(ReflectionUtils.getConstructor(clazz, parameterTypes)); } @Override public boolean isValid() { return true; } @Override public Size apply(MethodVisitor methodVisitor, Context implementationContext) { methodVisitor.visitTypeInsn(Opcodes.NEW, constructor.getDeclaringType().getInternalName()); return new StackManipulation.Compound( Duplication.SINGLE, arguments, MethodInvocation.invoke(constructor)) .apply(methodVisitor, implementationContext); } @Override public String toString() { return "NewObject(" + constructor + ", " + arguments + ")"; } }