forked from jasder/antlr
Merge branch 'master' into accurate-ctx-sensitive-check
This commit is contained in:
commit
015b9c900e
|
@ -1,55 +1,320 @@
|
||||||
|
/*
|
||||||
|
* [The "BSD license"]
|
||||||
|
* Copyright (c) 2014 Terence Parr
|
||||||
|
* Copyright (c) 2014 Sam Harwell
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package org.antlr.v4.runtime;
|
package org.antlr.v4.runtime;
|
||||||
|
|
||||||
/** Because targets can be updated at different times than the core tool, which includes this Java target,
|
import org.antlr.v4.runtime.misc.NotNull;
|
||||||
* we created this runtime information object. The goal is to test compatibility of the generated parser and
|
import org.antlr.v4.runtime.misc.Nullable;
|
||||||
* the runtime library for that target it executes with.
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides access to the current version of the ANTLR 4 runtime
|
||||||
|
* library as compile-time and runtime constants, along with methods for
|
||||||
|
* checking for matching version numbers and notifying listeners in the case
|
||||||
|
* where a version mismatch is detected.
|
||||||
*
|
*
|
||||||
* Targets generate a tiny bit of code that executes upon loading of
|
* <p>
|
||||||
* a parser or lexer, which should check its version against what is contained in this object. For example,
|
* The runtime version information is provided by {@link #VERSION} and
|
||||||
* here is something from the Java templates:
|
* {@link #getRuntimeVersion()}. Detailed information about these values is
|
||||||
|
* provided in the documentation for each member.</p>
|
||||||
*
|
*
|
||||||
* public class <parser.name> extends <superClass> {
|
* <p>
|
||||||
* static { RuntimeMetaData.checkVersion("<file.ANTLRVersion>"); }
|
* The runtime version check is implemented by {@link #checkVersion}. Detailed
|
||||||
* ...
|
* information about incorporating this call into user code, as well as its use
|
||||||
|
* in generated code, is provided in the documentation for the method.</p>
|
||||||
*
|
*
|
||||||
* This way, previous versions of target X that are incompatible (e.g., 4.1 and 4.2) will result in
|
* <p>
|
||||||
* a warning emitted through the appropriate channel for that target. This can be a msg to stderr or
|
* By default, the {@link DefaultListener#INSTANCE} listener is automatically
|
||||||
* an exception; it's up to the target developer. I have decided to throw an exception for Java target
|
* registered. As long as the default listener is registered, it will always be
|
||||||
* as a message might not be seen if the parser were embedded in a server or something.
|
* the last listener notified in the event of a version mismatch. This behavior
|
||||||
|
* ensures the exception it throws will not prevent {@link #checkVersion} from
|
||||||
|
* notifying custom listeners registered by a user. This default listener may be
|
||||||
|
* removed by calling {@link #removeListener} for
|
||||||
|
* {@link DefaultListener#INSTANCE} or {@link #clearListeners}. If required, it
|
||||||
|
* may be re-registered by calling {@link #addListener}.</p>
|
||||||
*/
|
*/
|
||||||
public class RuntimeMetaData {
|
public class RuntimeMetaData {
|
||||||
public static class ANTLRVersionMismatchException extends RuntimeException {
|
/**
|
||||||
public String generatedCodeVersion;
|
* This class provides detailed information about a mismatch between the
|
||||||
public String runtimeLibVersion;
|
* version of the tool a parser was generated with, the version of the
|
||||||
|
* runtime a parser was compiled against, and/or the currently executing
|
||||||
public ANTLRVersionMismatchException(String message, String generatedCodeVersion, String runtimeLibVersion) {
|
* version of the runtime.
|
||||||
super(message);
|
*
|
||||||
this.generatedCodeVersion = generatedCodeVersion;
|
* @see #checkVersion
|
||||||
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) {
|
public static class VersionMismatchException extends RuntimeException {
|
||||||
// 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
|
* The version of the ANTLR 4 Tool a parser was generated with. This
|
||||||
// being explicit. Later we can always build a more sophisticated versioning check.
|
* value may be {@code null} if {@link #checkVersion} was called from
|
||||||
if ( (toolVersion.startsWith("4.2.") || toolVersion.equals("4.2")) ) {
|
* user-defined code instead of a call automatically included in the
|
||||||
return;
|
* generated parser.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public final String generatingToolVersion;
|
||||||
|
/**
|
||||||
|
* The version of the ANTLR 4 Runtime library the parser and/or user
|
||||||
|
* code was compiled against.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public final String compileTimeRuntimeVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance of the {@link VersionMismatchException}
|
||||||
|
* class with the specified detailed information about a mismatch
|
||||||
|
* between ANTLR tool and runtime versions used by a parser.
|
||||||
|
*
|
||||||
|
* @param message the detail message. The detail message is saved for
|
||||||
|
* later retrieval by the {@link #getMessage()} method.
|
||||||
|
* @param generatingToolVersion The version of the ANTLR 4 Tool a parser
|
||||||
|
* was generated with, or {@code null} if the version check was not part
|
||||||
|
* of the automatically-generated parser code
|
||||||
|
* @param compileTimeRuntimeVersion The version of the ANTLR 4 Runtime
|
||||||
|
* library the code was compiled against
|
||||||
|
*/
|
||||||
|
VersionMismatchException(@NotNull String message, @Nullable String generatingToolVersion, @NotNull String compileTimeRuntimeVersion) {
|
||||||
|
super(message);
|
||||||
|
this.generatingToolVersion = generatingToolVersion;
|
||||||
|
this.compileTimeRuntimeVersion = compileTimeRuntimeVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface defines a listener which handles notifications about
|
||||||
|
* mismatched ANTLR Tool and/or Runtime versions.
|
||||||
|
*/
|
||||||
|
public interface Listener {
|
||||||
|
/**
|
||||||
|
* Report a version mismatch which was detected by
|
||||||
|
* {@link #checkDetails}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Implementations of this method may, but are not required to, throw
|
||||||
|
* the provided exception. Note that if a registered listener throws the
|
||||||
|
* provided exception during the handling of this event, the following
|
||||||
|
* will be impacted:</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>The lexer or parser which called {@link #checkVersion} will be
|
||||||
|
* unusable due to throwing an exception in a static initializer
|
||||||
|
* block.</li>
|
||||||
|
* <li>No additional registered listeners will be notified about the
|
||||||
|
* version mismatch. Since the default {@link DefaultListener} instance
|
||||||
|
* is always the last listener called (unless it is unregistered), the
|
||||||
|
* exception it throws will not affect the execution of any other
|
||||||
|
* registered listeners.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param ex a {@link VersionMismatchException} instance containing
|
||||||
|
* detailed information about the specific version mismatch detected
|
||||||
|
*/
|
||||||
|
void reportVersionMismatch(@NotNull VersionMismatchException ex)
|
||||||
|
throws VersionMismatchException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a default implementation of {@link Listener} which
|
||||||
|
* responds to mismatched versions by throwing the provided
|
||||||
|
* {@link VersionMismatchException}.
|
||||||
|
*/
|
||||||
|
public static class DefaultListener implements Listener {
|
||||||
|
/**
|
||||||
|
* A default instance of {@link ConsoleListener} which is automatically
|
||||||
|
* registered to receive version mismatch events.
|
||||||
|
*/
|
||||||
|
public static final DefaultListener INSTANCE = new DefaultListener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void reportVersionMismatch(@NotNull VersionMismatchException ex) throws VersionMismatchException {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of listeners registered to receive notifications of mismatched
|
||||||
|
* ANTLR versions.
|
||||||
|
*/
|
||||||
|
private static final Collection<Listener> listeners = new CopyOnWriteArraySet<Listener>();
|
||||||
|
static {
|
||||||
|
listeners.add(DefaultListener.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a listener to receive notifications of mismatched ANTLR
|
||||||
|
* versions. This method ensures that as long as
|
||||||
|
* {@link DefaultListener#INSTANCE} is registered as a listener, it will
|
||||||
|
* always be the last listener notified of mismatched versions.
|
||||||
|
*
|
||||||
|
* @param listener the listener to notify if mismatched ANTLR versions are
|
||||||
|
* detected
|
||||||
|
*
|
||||||
|
* @see #checkVersion
|
||||||
|
*/
|
||||||
|
public static synchronized void addListener(@NotNull Listener listener) {
|
||||||
|
boolean containedDefault = listeners.remove(DefaultListener.INSTANCE);
|
||||||
|
listeners.add(listener);
|
||||||
|
if (containedDefault) {
|
||||||
|
listeners.add(DefaultListener.INSTANCE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a specific listener registered to receive notifications of
|
||||||
|
* mismatched ANTLR versions.
|
||||||
|
*
|
||||||
|
* @param listener the listener to remove
|
||||||
|
* @return {@code true} if the listener was removed; otherwise,
|
||||||
|
* {@code false} if the specified listener was not found in the list of
|
||||||
|
* registered listeners
|
||||||
|
*/
|
||||||
|
public static synchronized boolean removeListener(@NotNull Listener listener) {
|
||||||
|
return listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all listeners registered to receive notifications of mismatched
|
||||||
|
* ANTLR versions.
|
||||||
|
*/
|
||||||
|
public static synchronized void clearListeners() {
|
||||||
|
listeners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compile-time constant containing the current version of the ANTLR 4
|
||||||
|
* runtime library.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This compile-time constant value allows generated parsers and other
|
||||||
|
* libraries to include a literal reference to the version of the ANTLR 4
|
||||||
|
* runtime library the code was compiled against.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* During development (between releases), this value contains the
|
||||||
|
* <em>expected</em> next release version. For official releases, the value
|
||||||
|
* will be the actual published version of the library.</p>
|
||||||
|
*/
|
||||||
|
public static final String VERSION = "4.2.3";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the currently executing version of the ANTLR 4 runtime library.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method provides runtime access to the {@link #VERSION} field, as
|
||||||
|
* opposed to directly referencing the field as a compile-time constant.</p>
|
||||||
|
*
|
||||||
|
* @return The currently executing version of the ANTLR 4 library
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static String getRuntimeVersion() {
|
||||||
|
return VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method provides the ability to detect mismatches between the version
|
||||||
|
* of ANTLR 4 used to generate a parser, the version of the ANTLR runtime a
|
||||||
|
* parser was compiled against, and the version of the ANTLR runtime which
|
||||||
|
* is currently executing.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The version check is designed to detect the following two specific
|
||||||
|
* scenarios.</p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>The ANTLR Tool version used for code generation does not match the
|
||||||
|
* currently executing runtime version.</li>
|
||||||
|
* <li>The ANTLR Runtime version referenced at the time a parser was
|
||||||
|
* compiled does not match the currently executing runtime version.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Starting with ANTLR 4.2.3, the code generator emits a call to this method
|
||||||
|
* using two constants in each generated lexer and parser: a hard-coded
|
||||||
|
* constant indicating the version of the tool used to generate the parser
|
||||||
|
* and a reference to the compile-time constant {@link #VERSION}. At
|
||||||
|
* runtime, this method is called during the initialization of the generated
|
||||||
|
* parser to detect mismatched versions, and notify the registered listeners
|
||||||
|
* prior to creating instances of the parser.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method does not perform any detection or filtering of semantic
|
||||||
|
* changes between tool and runtime versions. It simply checks for a simple
|
||||||
|
* version match and notifies the registered listeners any time a difference
|
||||||
|
* is detected.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that some breaking changes between releases could result in other
|
||||||
|
* types of runtime exceptions, such as a {@link LinkageError}, prior to
|
||||||
|
* calling this method. In these cases, the underlying version mismatch will
|
||||||
|
* not be reported to the listeners. This method is primarily intended to
|
||||||
|
* notify users of potential semantic changes between releases that do not
|
||||||
|
* result in binary compatibility problems which would be detected by the
|
||||||
|
* class loader. As with semantic changes, changes which break binary
|
||||||
|
* compatibility between releases are mentioned in the release notes
|
||||||
|
* accompanying the affected release.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <strong>Additional note for target developers:</strong> The version check
|
||||||
|
* implemented by this class is designed to address specific compatibility
|
||||||
|
* concerns that may arise during the execution of Java applications. Other
|
||||||
|
* targets should consider the implementation of this method in the context
|
||||||
|
* of that target's known execution environment, which may or may not
|
||||||
|
* resemble the design provided for the Java target.</p>
|
||||||
|
*
|
||||||
|
* @param toolVersion The version of the tool used to generate a parser.
|
||||||
|
* This value may be null when called from user code that was not generated
|
||||||
|
* by, and does not reference, the ANTLR 4 Tool itself.
|
||||||
|
* @param compileTimeVersion The version of the runtime the parser was
|
||||||
|
* compiled against. This should always be passed using a direct reference
|
||||||
|
* to {@link #VERSION}.
|
||||||
|
*/
|
||||||
|
public static void checkVersion(@Nullable String toolVersion, @NotNull String compileTimeVersion) {
|
||||||
|
boolean report = false;
|
||||||
|
String message = null;
|
||||||
|
if (toolVersion != null && !VERSION.equals(toolVersion)) {
|
||||||
|
report = true;
|
||||||
|
message = String.format("ANTLR Tool version %s used for code generation does not match the current runtime version %s", toolVersion, VERSION);
|
||||||
|
}
|
||||||
|
else if (!VERSION.equals(compileTimeVersion)) {
|
||||||
|
report = true;
|
||||||
|
message = String.format("ANTLR Runtime version %s used for parser compilation does not match the current runtime version %s", compileTimeVersion, VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (report) {
|
||||||
|
VersionMismatchException ex = new VersionMismatchException(message, toolVersion, compileTimeVersion);
|
||||||
|
for (Listener listener : listeners) {
|
||||||
|
listener.reportVersionMismatch(ex);
|
||||||
}
|
}
|
||||||
if ( !VERSION.equals(toolVersion) ) {
|
|
||||||
throw new ANTLRVersionMismatchException("ANTLR runtime and generated code versions disagree",toolVersion,VERSION);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,7 +214,7 @@ 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>"); }
|
static { RuntimeMetaData.checkVersion("<file.ANTLRVersion>", RuntimeMetaData.VERSION); }
|
||||||
|
|
||||||
protected static final DFA[] _decisionToDFA;
|
protected static final DFA[] _decisionToDFA;
|
||||||
protected static final PredictionContextCache _sharedContextCache =
|
protected static final PredictionContextCache _sharedContextCache =
|
||||||
|
@ -816,7 +816,8 @@ 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>"); }
|
static { RuntimeMetaData.checkVersion("<lexerFile.ANTLRVersion>", RuntimeMetaData.VERSION); }
|
||||||
|
|
||||||
protected static final DFA[] _decisionToDFA;
|
protected static final DFA[] _decisionToDFA;
|
||||||
protected static final PredictionContextCache _sharedContextCache =
|
protected static final PredictionContextCache _sharedContextCache =
|
||||||
new PredictionContextCache();
|
new PredictionContextCache();
|
||||||
|
|
Loading…
Reference in New Issue