Merge pull request #569 from parrt/runtime-tool-version-sync

add tool-template and generatedcode-runtime version compatibility check
This commit is contained in:
Terence Parr 2014-05-28 18:02:14 -07:00
commit f3fcf5a712
7 changed files with 91 additions and 12 deletions

View File

@ -0,0 +1,55 @@
package org.antlr.v4.runtime;
/** Because targets can be updated at different times than the core tool, which includes this Java target,
* we created this runtime information object. The goal is to test compatibility of the generated parser and
* the runtime library for that target it executes with.
*
* Targets generate a tiny bit of code that executes upon loading of
* a parser or lexer, which should check its version against what is contained in this object. For example,
* here is something from the Java templates:
*
* public class <parser.name> extends <superClass> {
* static { RuntimeMetaData.checkVersion("<file.ANTLRVersion>"); }
* ...
*
* This way, previous versions of target X that are incompatible (e.g., 4.1 and 4.2) will result in
* a warning emitted through the appropriate channel for that target. This can be a msg to stderr or
* an exception; it's up to the target developer. I have decided to throw an exception for Java target
* as a message might not be seen if the parser were embedded in a server or something.
*/
public class RuntimeMetaData {
public static class ANTLRVersionMismatchException extends RuntimeException {
public String generatedCodeVersion;
public String runtimeLibVersion;
public ANTLRVersionMismatchException(String message, String generatedCodeVersion, String runtimeLibVersion) {
super(message);
this.generatedCodeVersion = generatedCodeVersion;
this.runtimeLibVersion = runtimeLibVersion;
}
@Override
public String getMessage() {
return super.getMessage()+": code version "+generatedCodeVersion+" != runtime version "+runtimeLibVersion;
}
}
/** Must match version of tool that generated recognizers */
public static final String VERSION = "4.3"; // use just "x.y" and don't include bug fix release number
/** As parser or lexer class is loaded, it checks that the version used to generate the code
* is compatible with the runtime version. ANTLR tool generates recognizers with a hardcoded string created by
* the tool during code gen. That version is passed to checkVersion().
*/
public static void checkVersion(String toolVersion) {
// I believe that 4.2-generated parsers are compatible with the runtime for 4.3 so no exception in this case.
// Technically, we don't need this check because 4.2 it doesn't actually check the version. ;) I am just
// being explicit. Later we can always build a more sophisticated versioning check.
if ( (toolVersion.startsWith("4.2.") || toolVersion.equals("4.2")) ) {
return;
}
if ( !VERSION.equals(toolVersion) ) {
throw new ANTLRVersionMismatchException("ANTLR runtime and generated code versions disagree",toolVersion,VERSION);
}
}
}

View File

@ -214,6 +214,8 @@ Parser(parser, funcs, atn, sempredFuncs, superClass) ::= <<
Parser_(parser, funcs, atn, sempredFuncs, ctor, superClass) ::= << Parser_(parser, funcs, atn, sempredFuncs, ctor, superClass) ::= <<
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class <parser.name> extends <superClass> { public class <parser.name> extends <superClass> {
static { RuntimeMetaData.checkVersion("<file.ANTLRVersion>"); }
protected static final DFA[] _decisionToDFA; protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache = protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache(); new PredictionContextCache();
@ -848,6 +850,7 @@ import org.antlr.v4.runtime.misc.*;
Lexer(lexer, atn, actionFuncs, sempredFuncs, superClass) ::= << Lexer(lexer, atn, actionFuncs, sempredFuncs, superClass) ::= <<
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class <lexer.name> extends <superClass> { public class <lexer.name> extends <superClass> {
static { RuntimeMetaData.checkVersion("<lexerFile.ANTLRVersion>"); }
protected static final DFA[] _decisionToDFA; protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache = protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache(); new PredictionContextCache();

View File

@ -90,7 +90,7 @@ public class Tool {
public static final String VERSION; public static final String VERSION;
static { static {
String version = Tool.class.getPackage().getImplementationVersion(); String version = Tool.class.getPackage().getImplementationVersion();
VERSION = version != null ? version : "4.x"; VERSION = version != null ? version : "4.3";
} }
public static final String GRAMMAR_EXTENSION = ".g4"; public static final String GRAMMAR_EXTENSION = ".g4";

View File

@ -30,6 +30,7 @@
package org.antlr.v4.codegen; package org.antlr.v4.codegen;
import org.antlr.v4.Tool;
import org.antlr.v4.tool.ast.GrammarAST; import org.antlr.v4.tool.ast.GrammarAST;
import org.stringtemplate.v4.STGroup; import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.StringRenderer; import org.stringtemplate.v4.StringRenderer;
@ -39,10 +40,6 @@ import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
/**
*
* @author Sam Harwell
*/
public class JavaTarget extends Target { public class JavaTarget extends Target {
protected static final String[] javaKeywords = { protected static final String[] javaKeywords = {
@ -63,6 +60,11 @@ public class JavaTarget extends Target {
super(gen, "Java"); super(gen, "Java");
} }
@Override
public String getVersion() {
return Tool.VERSION; // Java and tool versions move in lock step
}
public Set<String> getBadWords() { public Set<String> getBadWords() {
if (badWords.isEmpty()) { if (badWords.isEmpty()) {
addBadWords(); addBadWords();

View File

@ -30,6 +30,7 @@
package org.antlr.v4.codegen; package org.antlr.v4.codegen;
import org.antlr.v4.Tool;
import org.antlr.v4.codegen.model.RuleFunction; import org.antlr.v4.codegen.model.RuleFunction;
import org.antlr.v4.codegen.model.SerializedATN; import org.antlr.v4.codegen.model.SerializedATN;
import org.antlr.v4.misc.Utils; import org.antlr.v4.misc.Utils;
@ -87,8 +88,20 @@ public abstract class Target {
return language; return language;
} }
/** ANTLR tool should check output templates / target are compatible with tool code generation.
* For now, a simple string match used on x.y of x.y.z scheme. We use a method to avoid mismatches
* between a template called VERSION. This value is checked against Tool.VERSION during load of templates.
*
* This additional method forces all targets 4.3 and beyond to add this method.
*/
public abstract String getVersion();
public STGroup getTemplates() { public STGroup getTemplates() {
if (templates == null) { if (templates == null) {
String version = getVersion();
if (version == null || !version.equals(Tool.VERSION)) {
gen.tool.errMgr.toolError(ErrorType.INCOMPATIBLE_TOOL_AND_TEMPLATES, version, Tool.VERSION, language);
}
templates = loadTemplates(); templates = loadTemplates();
} }

View File

@ -169,6 +169,12 @@ public enum ErrorType {
* no mapping to template name for output model class '<em>class</em>'</p> * no mapping to template name for output model class '<em>class</em>'</p>
*/ */
NO_MODEL_TO_TEMPLATE_MAPPING(34, "no mapping to template name for output model class '<arg>'", ErrorSeverity.ERROR), NO_MODEL_TO_TEMPLATE_MAPPING(34, "no mapping to template name for output model class '<arg>'", ErrorSeverity.ERROR),
/**
* Compiler Error 35.
*
* <p>templates/target and tool aren't compatible</p>
*/
INCOMPATIBLE_TOOL_AND_TEMPLATES(35, "<arg3> code generation target requires ANTLR <arg2>; it can't be loaded by the current ANTLR <arg>", ErrorSeverity.ERROR),
/* /*
* Grammar errors * Grammar errors