| /* |
| * 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; |
| } |
| } |
| } |