mirror of https://github.com/apache/lucene.git
LUCENE-7800: Remove code that potentially rethrows checked exceptions from methods that don't declare them ("sneaky throw" hack).
This commit is contained in:
parent
c9bdce937a
commit
5a50887a4b
|
@ -97,6 +97,12 @@ Other
|
|||
|
||||
======================= Lucene 6.7.0 =======================
|
||||
|
||||
Other
|
||||
|
||||
* LUCENE-7800: Remove code that potentially rethrows checked exceptions
|
||||
from methods that don't declare them ("sneaky throw" hack). (Robert Muir,
|
||||
Uwe Schindler, Dawid Weiss)
|
||||
|
||||
======================= Lucene 6.6.0 =======================
|
||||
|
||||
New Features
|
||||
|
|
|
@ -31,6 +31,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
package org.tartarus.snowball;
|
||||
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
|
||||
import org.apache.lucene.util.ArrayUtil;
|
||||
|
||||
/**
|
||||
|
@ -313,8 +315,10 @@ public abstract class SnowballProgram {
|
|||
boolean res = false;
|
||||
try {
|
||||
res = (boolean) w.method.invokeExact(this);
|
||||
} catch (Error | RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
rethrow(e);
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
cursor = c + w.s_size;
|
||||
if (res) return w.result;
|
||||
|
@ -376,8 +380,10 @@ public abstract class SnowballProgram {
|
|||
boolean res = false;
|
||||
try {
|
||||
res = (boolean) w.method.invokeExact(this);
|
||||
} catch (Error | RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
rethrow(e);
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
cursor = c - w.s_size;
|
||||
if (res) return w.result;
|
||||
|
@ -485,15 +491,5 @@ extern void debug(struct SN_env * z, int number, int line_count)
|
|||
printf("'\n");
|
||||
}
|
||||
*/
|
||||
|
||||
// Hack to rethrow unknown Exceptions from {@link MethodHandle#invoke}:
|
||||
private static void rethrow(Throwable t) {
|
||||
SnowballProgram.<Error>rethrow0(t);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Throwable> void rethrow0(Throwable t) throws T {
|
||||
throw (T) t;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.lucene.util;
|
|||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
|
||||
/**
|
||||
* An AttributeFactory creates instances of {@link AttributeImpl}s.
|
||||
|
@ -28,8 +29,14 @@ public abstract class AttributeFactory {
|
|||
|
||||
/**
|
||||
* Returns an {@link AttributeImpl} for the supplied {@link Attribute} interface class.
|
||||
*
|
||||
* @throws UndeclaredThrowableException A wrapper runtime exception thrown if the
|
||||
* constructor of the attribute class throws a checked exception.
|
||||
* Note that attributes should not throw or declare
|
||||
* checked exceptions; this may be verified and fail early in the future.
|
||||
*/
|
||||
public abstract AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass);
|
||||
public abstract AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass)
|
||||
throws UndeclaredThrowableException;
|
||||
|
||||
/**
|
||||
* Returns a correctly typed {@link MethodHandle} for the no-arg ctor of the given class.
|
||||
|
@ -61,17 +68,18 @@ public abstract class AttributeFactory {
|
|||
};
|
||||
|
||||
DefaultAttributeFactory() {}
|
||||
|
||||
|
||||
@Override
|
||||
public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) {
|
||||
try {
|
||||
return (AttributeImpl) constructors.get(attClass).invokeExact();
|
||||
} catch (Throwable t) {
|
||||
rethrow(t);
|
||||
throw new AssertionError();
|
||||
} catch (Error | RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Class<? extends AttributeImpl> findImplClass(Class<? extends Attribute> attClass) {
|
||||
try {
|
||||
return Class.forName(attClass.getName() + "Impl", true, attClass.getClassLoader()).asSubclass(AttributeImpl.class);
|
||||
|
@ -138,23 +146,12 @@ public abstract class AttributeFactory {
|
|||
protected A createInstance() {
|
||||
try {
|
||||
return (A) constr.invokeExact();
|
||||
} catch (Throwable t) {
|
||||
rethrow(t);
|
||||
throw new AssertionError();
|
||||
} catch (Error | RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Hack to rethrow unknown Exceptions from {@link MethodHandle#invoke}:
|
||||
// TODO: remove the impl in test-framework, this one is more elegant :-)
|
||||
static void rethrow(Throwable t) {
|
||||
AttributeFactory.<Error>rethrow0(t);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Throwable> void rethrow0(Throwable t) throws T {
|
||||
throw (T) t;
|
||||
}
|
||||
|
||||
}
|
|
@ -188,15 +188,20 @@ public final class JavascriptCompiler {
|
|||
private Expression compileExpression(ClassLoader parent) throws ParseException {
|
||||
final Map<String, Integer> externalsMap = new LinkedHashMap<>();
|
||||
final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
|
||||
generateClass(getAntlrParseTree(), classWriter, externalsMap);
|
||||
|
||||
|
||||
try {
|
||||
generateClass(getAntlrParseTree(), classWriter, externalsMap);
|
||||
|
||||
final Class<? extends Expression> evaluatorClass = new Loader(parent)
|
||||
.define(COMPILED_EXPRESSION_CLASS, classWriter.toByteArray());
|
||||
final Constructor<? extends Expression> constructor = evaluatorClass.getConstructor(String.class, String[].class);
|
||||
|
||||
return constructor.newInstance(sourceText, externalsMap.keySet().toArray(new String[externalsMap.size()]));
|
||||
} catch (RuntimeException re) {
|
||||
if (re.getCause() instanceof ParseException) {
|
||||
throw (ParseException)re.getCause();
|
||||
}
|
||||
throw re;
|
||||
} catch (ReflectiveOperationException exception) {
|
||||
throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + sourceText + ").", exception);
|
||||
}
|
||||
|
@ -209,20 +214,13 @@ public final class JavascriptCompiler {
|
|||
* @throws ParseException on failure to parse
|
||||
*/
|
||||
private ParseTree getAntlrParseTree() throws ParseException {
|
||||
try {
|
||||
final ANTLRInputStream antlrInputStream = new ANTLRInputStream(sourceText);
|
||||
final JavascriptErrorHandlingLexer javascriptLexer = new JavascriptErrorHandlingLexer(antlrInputStream);
|
||||
javascriptLexer.removeErrorListeners();
|
||||
final JavascriptParser javascriptParser = new JavascriptParser(new CommonTokenStream(javascriptLexer));
|
||||
javascriptParser.removeErrorListeners();
|
||||
javascriptParser.setErrorHandler(new JavascriptParserErrorStrategy());
|
||||
return javascriptParser.compile();
|
||||
} catch (RuntimeException re) {
|
||||
if (re.getCause() instanceof ParseException) {
|
||||
throw (ParseException)re.getCause();
|
||||
}
|
||||
throw re;
|
||||
}
|
||||
final ANTLRInputStream antlrInputStream = new ANTLRInputStream(sourceText);
|
||||
final JavascriptErrorHandlingLexer javascriptLexer = new JavascriptErrorHandlingLexer(antlrInputStream);
|
||||
javascriptLexer.removeErrorListeners();
|
||||
final JavascriptParser javascriptParser = new JavascriptParser(new CommonTokenStream(javascriptLexer));
|
||||
javascriptParser.removeErrorListeners();
|
||||
javascriptParser.setErrorHandler(new JavascriptParserErrorStrategy());
|
||||
return javascriptParser.compile();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,51 +289,56 @@ public final class JavascriptCompiler {
|
|||
boolean parens = ctx.LP() != null && ctx.RP() != null;
|
||||
Method method = parens ? functions.get(text) : null;
|
||||
|
||||
if (method != null) {
|
||||
int arity = method.getParameterTypes().length;
|
||||
|
||||
if (arguments != arity) {
|
||||
throwChecked(new ParseException(
|
||||
"Invalid expression '" + sourceText + "': Expected (" +
|
||||
arity + ") arguments for function call (" + text + "), but found (" + arguments + ").",
|
||||
ctx.start.getStartIndex()));
|
||||
}
|
||||
|
||||
typeStack.push(Type.DOUBLE_TYPE);
|
||||
|
||||
for (int argument = 0; argument < arguments; ++argument) {
|
||||
visit(ctx.expression(argument));
|
||||
}
|
||||
|
||||
typeStack.pop();
|
||||
|
||||
gen.invokeStatic(Type.getType(method.getDeclaringClass()),
|
||||
org.objectweb.asm.commons.Method.getMethod(method));
|
||||
|
||||
gen.cast(Type.DOUBLE_TYPE, typeStack.peek());
|
||||
} else if (!parens || arguments == 0 && text.contains(".")) {
|
||||
int index;
|
||||
|
||||
text = normalizeQuotes(ctx.getText());
|
||||
|
||||
if (externalsMap.containsKey(text)) {
|
||||
index = externalsMap.get(text);
|
||||
try {
|
||||
if (method != null) {
|
||||
int arity = method.getParameterTypes().length;
|
||||
|
||||
if (arguments != arity) {
|
||||
throw new ParseException(
|
||||
"Invalid expression '" + sourceText + "': Expected (" +
|
||||
arity + ") arguments for function call (" + text + "), but found (" + arguments + ").",
|
||||
ctx.start.getStartIndex());
|
||||
}
|
||||
|
||||
typeStack.push(Type.DOUBLE_TYPE);
|
||||
|
||||
for (int argument = 0; argument < arguments; ++argument) {
|
||||
visit(ctx.expression(argument));
|
||||
}
|
||||
|
||||
typeStack.pop();
|
||||
|
||||
gen.invokeStatic(Type.getType(method.getDeclaringClass()),
|
||||
org.objectweb.asm.commons.Method.getMethod(method));
|
||||
|
||||
gen.cast(Type.DOUBLE_TYPE, typeStack.peek());
|
||||
} else if (!parens || arguments == 0 && text.contains(".")) {
|
||||
int index;
|
||||
|
||||
text = normalizeQuotes(ctx.getText());
|
||||
|
||||
if (externalsMap.containsKey(text)) {
|
||||
index = externalsMap.get(text);
|
||||
} else {
|
||||
index = externalsMap.size();
|
||||
externalsMap.put(text, index);
|
||||
}
|
||||
|
||||
gen.loadArg(0);
|
||||
gen.push(index);
|
||||
gen.arrayLoad(FUNCTION_VALUES_TYPE);
|
||||
gen.invokeVirtual(FUNCTION_VALUES_TYPE, DOUBLE_VAL_METHOD);
|
||||
gen.cast(Type.DOUBLE_TYPE, typeStack.peek());
|
||||
} else {
|
||||
index = externalsMap.size();
|
||||
externalsMap.put(text, index);
|
||||
throw new ParseException("Invalid expression '" + sourceText + "': Unrecognized function call (" +
|
||||
text + ").", ctx.start.getStartIndex());
|
||||
}
|
||||
|
||||
gen.loadArg(0);
|
||||
gen.push(index);
|
||||
gen.arrayLoad(FUNCTION_VALUES_TYPE);
|
||||
gen.invokeVirtual(FUNCTION_VALUES_TYPE, DOUBLE_VAL_METHOD);
|
||||
gen.cast(Type.DOUBLE_TYPE, typeStack.peek());
|
||||
} else {
|
||||
throwChecked(new ParseException("Invalid expression '" + sourceText + "': Unrecognized function call (" +
|
||||
text + ").", ctx.start.getStartIndex()));
|
||||
return null;
|
||||
} catch (ParseException e) {
|
||||
// The API doesn't allow checked exceptions here, so propagate up the stack. This is unwrapped
|
||||
// in getAntlrParseTree.
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -623,16 +626,6 @@ public final class JavascriptCompiler {
|
|||
throw new IllegalStateException("Invalid expected type: " + typeStack.peek());
|
||||
}
|
||||
}
|
||||
|
||||
/** Needed to throw checked ParseException in this visitor (that does not allow it). */
|
||||
private void throwChecked(Throwable t) {
|
||||
this.<Error>throwChecked0(t);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends Throwable> void throwChecked0(Throwable t) throws T {
|
||||
throw (T) t;
|
||||
}
|
||||
}.visit(parseTree);
|
||||
|
||||
gen.returnValue();
|
||||
|
|
Loading…
Reference in New Issue