blob: 648e19b28219a75cff71fc64d88e750e2c6ec169 [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 org.apache.flex.tools.codecoverage.reporter.swf;
import org.apache.flex.abc.ABCConstants;
import org.apache.flex.abc.Pool;
import org.apache.flex.abc.instructionlist.InstructionList;
import org.apache.flex.abc.semantics.ExceptionInfo;
import org.apache.flex.abc.semantics.Instruction;
import org.apache.flex.abc.semantics.Label;
import org.apache.flex.abc.semantics.MethodBodyInfo;
import org.apache.flex.abc.semantics.MethodInfo;
import org.apache.flex.abc.semantics.Name;
import org.apache.flex.abc.visitors.IMethodBodyVisitor;
import org.apache.flex.abc.visitors.IMethodVisitor;
import org.apache.flex.abc.visitors.ITraitsVisitor;
import org.apache.flex.abc.visitors.NilABCVisitor;
import org.apache.flex.abc.visitors.NilVisitors;
import org.apache.flex.tools.codecoverage.reporter.CoverageData;
/**
* Gather all the debug lines and method in a SWF file.
* Look for debug_file and debug_line op codes and record them so we know all
* of possible lines in the SWF that can be executed.
*
*/
public class DebugLineVisitor extends NilABCVisitor
{
private final Pool<String> stringPool = new Pool<String>(Pool.DefaultType.HasDefaultZero);
private final CoverageData coverageData;
/**
* Constructor
*
* @param coverageData The coverage data where debug lines will be added
* as unexecuted lines. If a line is already in the coverage data and is
* marked as executed, then it will remain an executed line.
*/
public DebugLineVisitor(final CoverageData coverageData)
{
this.coverageData = coverageData;
}
@Override
public IMethodVisitor visitMethod(MethodInfo minfo)
{
return new MethodVisitor();
}
@Override
public void visitPooledString(String s)
{
stringPool.add(s);
}
/**
* Visit all the methods in the ABC.
*/
private class MethodVisitor implements IMethodVisitor
{
@Override
public void visitEnd()
{
}
@Override
public void visit()
{
}
@Override
public IMethodBodyVisitor visitBody(MethodBodyInfo mbInfo)
{
return new MethodBodyVisitor(mbInfo);
}
}
/**
* Visit all the method bodies in the ABC.
*/
private class MethodBodyVisitor implements IMethodBodyVisitor
{
private final MethodBodyInfo mbInfo;
private String currentFilename;
public MethodBodyVisitor(MethodBodyInfo mbInfo)
{
this.mbInfo = mbInfo;
}
@Override
public void visitEnd()
{
}
@Override
public void labelCurrent(Label l)
{
}
@Override
public void labelNext(Label l)
{
}
@Override
public void visit()
{
}
@Override
public int visitException(Label from, Label to, Label target,
Name exception_type, Name catch_var)
{
return mbInfo.addExceptionInfo(new ExceptionInfo(from, to, target,
exception_type, catch_var));
}
@Override
public void visitInstructionList(InstructionList new_list)
{
for (Instruction inst : new_list.getInstructions())
{
int opcode = inst.getOpcode();
assert opcode != ABCConstants.OP_debugfile &&
opcode != ABCConstants.OP_debugline :
"unexpected debug opcode";
}
}
@Override
public void visitInstruction(int opcode)
{
assert opcode != ABCConstants.OP_debugfile &&
opcode != ABCConstants.OP_debugline :
"unexpected debug opcode";
}
@Override
public void visitInstruction(int opcode, int immediate_operand)
{
if (opcode == ABCConstants.OP_debugfile)
{
recordDebugFile(immediate_operand);
}
else if (opcode == ABCConstants.OP_debugline)
{
recordDebugLine(immediate_operand);
}
}
@Override
public void visitInstruction(int opcode, Object[] operands)
{
assert opcode != ABCConstants.OP_debugfile &&
opcode != ABCConstants.OP_debugline :
"unexpected debug opcode";
}
@Override
public void visitInstruction(int opcode, Object single_operand)
{
assert opcode != ABCConstants.OP_debugline :
"unexpected debug opcode";
if (opcode == ABCConstants.OP_debugfile)
recordDebugFile((String)single_operand);
}
@Override
public void visitInstruction(Instruction instruction)
{
int opcode = instruction.getOpcode();
assert opcode != ABCConstants.OP_debugfile &&
opcode != ABCConstants.OP_debugline :
"unexpected debug opcode";
}
@Override
public ITraitsVisitor visitTraits()
{
return NilVisitors.NIL_TRAITS_VISITOR;
}
/**
* Record a debugfile string
*
* @param filenameIndex Index of filename in the string pool.
*/
private void recordDebugFile(final int filenameIndex)
{
recordDebugFile(stringPool.getValues().get(filenameIndex));
}
/**
* Set the filename in the context for debug lines that
* come after it.
*
* @param filename The debugfile filename.
*/
private void recordDebugFile(final String filename)
{
currentFilename = filename;
}
/**
* Record a debug line into the coverage data.
*
* @param lineNumber The line number of the debug line in
* the file that is set by the last debugfile opcode.
*/
private void recordDebugLine(final int lineNumber)
{
assert currentFilename != null :
"currentFilename must have been set by a debugFile instruction";
String methodName = mbInfo.getMethodInfo().getMethodName();
coverageData.addLine(currentFilename, lineNumber, methodName);
}
}
}