/*
* Copyright 2009 Google Inc.
*
* 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.jstestdriver.coverage;
import com.google.inject.Inject;
import com.google.jstestdriver.coverage.es3.ES3InstrumentLexer;
import com.google.jstestdriver.coverage.es3.ES3InstrumentParser;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.TokenRewriteStream;
import org.antlr.stringtemplate.StringTemplateGroup;
import java.io.CharArrayReader;
import java.util.Collections;
import java.util.List;
/**
* Decorates the source code with coverage instrumentation.
* @author corysmith@google.com (Cory Smith)
*/
public class CodeInstrumentor implements Instrumentor {
/** Thrown when instrumentation fails.*/
public class InstrumentationException extends Error {
private static final long serialVersionUID = 6999317302885453217L;
public InstrumentationException(String path, Throwable cause) {
super("error instrumenting " + path, cause);
}
}
private static final char[] TEMPLATE =
("group TestRewrite;\n" +
"init_instrument(stmt, hash, name, lines) ::= \"LCOV_<hash>=" +
"LCOV.initNoop(<name>,0,<lines>);<stmt>\"" +
"instrument(stmt, hash, ln) ::= \"LCOV_<hash>[<ln>]++; <stmt>\"" +
"pass(stmt) ::= \"<stmt>\"").toCharArray();
private final CoverageNameMapper mapper;
@Inject
public CodeInstrumentor(CoverageNameMapper mapper) {
this.mapper = mapper;
}
public InstrumentedCode instrument(Code code) {
StringTemplateGroup templates = new StringTemplateGroup(new CharArrayReader(TEMPLATE));
ANTLRStringStream stream = new ANTLRStringStream(code.getSourceCode());
Integer fileId = mapper.map(code.getFilePath());
String mappedName = String.valueOf(fileId);
stream.name = mappedName;
ES3InstrumentLexer lexer = new ES3InstrumentLexer(stream);
TokenRewriteStream tokens = new TokenRewriteStream(lexer);
ES3InstrumentParser parser = new ES3InstrumentParser(tokens);
parser.setTemplateLib(templates);
try {
parser.program();
} catch (Exception e) {
throw new InstrumentationException(code.getFilePath(), e);
}
List<Integer> executableLines = parser.linesMap.get(mappedName);
return new InstrumentedCode(fileId,
code.getFilePath(),
executableLines == null ?
Collections.<Integer>emptyList() : executableLines,
tokens.toString());
}
}