blob: ccebb2b5dfbf42f17af5582bdfa340a812c0c83b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.facebook.presto.bytecode;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.facebook.presto.bytecode.debug.LocalVariableNode;
import com.facebook.presto.bytecode.instruction.LabelNode;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import static com.facebook.presto.bytecode.BytecodeUtils.checkArgument;
import static java.util.Objects.requireNonNull;
public class MethodGenerationContext {
private final MethodVisitor methodVisitor;
private final Set<Scope> allEnteredScopes = new LinkedHashSet<>();
private final Deque<ScopeContext> scopes = new ArrayDeque<>();
private final Map<Variable, Integer> variableSlots = new HashMap<>();
private int nextSlot;
private int currentLineNumber = -1;
public MethodGenerationContext(MethodVisitor methodVisitor) {
this.methodVisitor = requireNonNull(methodVisitor, "methodVisitor is null");
}
public void enterScope(Scope scope) {
requireNonNull(scope, "scope is null");
checkArgument(!allEnteredScopes.contains(scope), "scope has already been entered");
allEnteredScopes.add(scope);
ScopeContext scopeContext = new ScopeContext(scope);
scopes.addLast(scopeContext);
for (Variable variable : scopeContext.getVariables()) {
checkArgument(!"this".equals(variable.getName()) || nextSlot == 0, "The 'this' variable must be in slot 0");
variableSlots.put(variable, nextSlot);
nextSlot += Type.getType(variable.getType().getType()).getSize();
}
scopeContext.getStartLabel().accept(methodVisitor, this);
}
public void exitScope(Scope scope) {
checkArgument(allEnteredScopes.contains(scope), "scope has not been entered");
checkArgument(!scopes.isEmpty() && scope == scopes.peekLast().getScope(), "Scope is not top of the stack");
ScopeContext scopeContext = scopes.removeLast();
scopeContext.getEndLabel().accept(methodVisitor, this);
for (Variable variable : scopeContext.getVariables()) {
new LocalVariableNode(variable, scopeContext.getStartLabel(), scopeContext.getEndLabel()).accept(methodVisitor, this);
}
variableSlots.keySet().removeAll(scopeContext.getVariables());
}
public int getVariableSlot(Variable variable) {
Integer slot = variableSlots.get(variable);
checkArgument(slot != null, "Variable '%s' has not been assigned a slot", variable);
return slot;
}
public boolean updateLineNumber(int lineNumber) {
if (lineNumber == currentLineNumber) {
return false;
}
currentLineNumber = lineNumber;
return true;
}
private static final class ScopeContext {
private final Scope scope;
private final List<Variable> variables;
private final LabelNode startLabel = new LabelNode("VariableStart");
private final LabelNode endLabel = new LabelNode("VariableEnd");
ScopeContext(Scope scope) {
this.scope = scope;
this.variables = List.copyOf(scope.getVariables());
}
public Scope getScope() {
return scope;
}
public List<Variable> getVariables() {
return variables;
}
public LabelNode getStartLabel() {
return startLabel;
}
public LabelNode getEndLabel() {
return endLabel;
}
}
}