Skip to content

Commit

Permalink
SSLR-393 Replace cglib by ASM
Browse files Browse the repository at this point in the history
To get rid of illegal reflective access.
  • Loading branch information
Godin committed Oct 27, 2020
1 parent 0b2731a commit 3d0519b
Show file tree
Hide file tree
Showing 7 changed files with 413 additions and 41 deletions.
5 changes: 0 additions & 5 deletions pom.xml
Expand Up @@ -126,11 +126,6 @@
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
5 changes: 3 additions & 2 deletions sslr-core/pom.xml
Expand Up @@ -18,8 +18,9 @@
<artifactId>jsr305</artifactId>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.0</version>
</dependency>

<dependency>
Expand Down
40 changes: 17 additions & 23 deletions sslr-core/src/main/java/com/sonar/sslr/api/typed/ActionParser.java
Expand Up @@ -20,12 +20,11 @@
package com.sonar.sslr.api.typed;

import com.sonar.sslr.api.RecognitionException;
import com.sonar.sslr.impl.typed.Interceptor;
import com.sonar.sslr.impl.typed.MethodInterceptor;
import com.sonar.sslr.impl.typed.GrammarBuilderInterceptor;
import com.sonar.sslr.impl.typed.ReflectionUtils;
import com.sonar.sslr.impl.typed.SyntaxTreeCreator;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.sonar.sslr.grammar.GrammarRuleKey;
import org.sonar.sslr.grammar.LexerlessGrammarBuilder;
import org.sonar.sslr.internal.matchers.InputBuffer;
Expand Down Expand Up @@ -56,18 +55,18 @@ public ActionParser(Charset charset, LexerlessGrammarBuilder b, Class grammarCla
this.charset = charset;

GrammarBuilderInterceptor grammarBuilderInterceptor = new GrammarBuilderInterceptor(b);
Enhancer grammarEnhancer = new Enhancer();
grammarEnhancer.setSuperclass(grammarClass);
grammarEnhancer.setCallback(grammarBuilderInterceptor);

ActionMethodInterceptor actionMethodInterceptor = new ActionMethodInterceptor(grammarBuilderInterceptor);
Enhancer actionEnhancer = new Enhancer();
actionEnhancer.setSuperclass(treeFactory.getClass());
actionEnhancer.setCallback(actionMethodInterceptor);

Object grammar = grammarEnhancer.create(
new Class[] {GrammarBuilder.class, treeFactory.getClass()},
new Object[] {grammarBuilderInterceptor, actionEnhancer.create()});
Object treeFactoryInterceptor = Interceptor.create(
treeFactory.getClass(),
new Class[]{},
new Object[]{},
new ActionMethodInterceptor(grammarBuilderInterceptor)
);
Object grammar = Interceptor.create(
grammarClass,
new Class[]{GrammarBuilder.class, treeFactory.getClass()},
new Object[]{grammarBuilderInterceptor, treeFactoryInterceptor},
grammarBuilderInterceptor
);

for (Method method : grammarClass.getMethods()) {
if (method.getDeclaringClass().equals(Object.class)) {
Expand Down Expand Up @@ -124,14 +123,9 @@ public ActionMethodInterceptor(GrammarBuilderInterceptor grammarBuilderIntercept
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getDeclaringClass().equals(Object.class)) {
return proxy.invokeSuper(obj, args);
}

grammarBuilderInterceptor.addAction(method, args.length);

return null;
public boolean intercept(Method method) {
grammarBuilderInterceptor.addAction(method, method.getParameterCount());
return true;
}

}
Expand Down
Expand Up @@ -26,8 +26,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.sonar.sslr.grammar.GrammarRuleKey;
import org.sonar.sslr.grammar.LexerlessGrammarBuilder;
import org.sonar.sslr.internal.vm.FirstOfExpression;
Expand Down Expand Up @@ -63,19 +62,13 @@ public GrammarBuilderInterceptor(LexerlessGrammarBuilder b) {
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getDeclaringClass().equals(Object.class)) {
return proxy.invokeSuper(obj, args);
}

public boolean intercept(Method method) {
if (buildingMethod != null) {
push(new DelayedRuleInvocationExpression(b, this, method));
return null;
return true;
}

buildingMethod = method;

return proxy.invokeSuper(obj, args);
return false;
}

@Override
Expand Down
168 changes: 168 additions & 0 deletions sslr-core/src/main/java/com/sonar/sslr/impl/typed/Interceptor.java
@@ -0,0 +1,168 @@
/*
* SonarSource Language Recognizer
* Copyright (C) 2010-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sonar.sslr.impl.typed;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.lang.reflect.Method;
import java.util.Arrays;

public class Interceptor {

private Interceptor() {
}

public static Object create(
Class<?> superClass,
Class<?>[] constructorParameterTypes,
Object[] constructorArguments,
MethodInterceptor interceptor
) {
ClassWriter cv = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
final String className = "GeneratedBySSLR";
final String superClassName = Type.getInternalName(superClass);
cv.visit(
Opcodes.V1_8,
Opcodes.ACC_PUBLIC,
className,
null,
superClassName,
null);

cv.visitField(
Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
"methodInterceptor",
Type.getDescriptor(MethodInterceptor.class),
null,
null);

cv.visitField(
Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
"methods",
Type.getDescriptor(Method[].class),
null,
null);

String constructorDescriptor = Type.getMethodDescriptor(
Type.getType(void.class),
Arrays.stream(constructorParameterTypes)
.map(Type::getType)
.toArray(Type[]::new));
MethodVisitor mv = cv.visitMethod(
Opcodes.ACC_PUBLIC,
"<init>",
constructorDescriptor,
null,
null);
mv.visitVarInsn(Opcodes.ALOAD, 0);
for (int i = 0; i < constructorParameterTypes.length; i++) {
mv.visitVarInsn(Type.getType(constructorParameterTypes[i]).getOpcode(Opcodes.ILOAD), 1 + i);
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
superClassName,
"<init>",
constructorDescriptor,
false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();

Method[] methods = superClass.getMethods();
for (int methodId = 0; methodId < methods.length; methodId++) {
Method method = methods[methodId];
if (Object.class.equals(method.getDeclaringClass())) {
continue;
}
if (method.getReturnType().isPrimitive()) {
throw new UnsupportedOperationException();
}
mv = cv.visitMethod(
Opcodes.ACC_PUBLIC,
method.getName(),
Type.getMethodDescriptor(method),
null,
null);

mv.visitFieldInsn(Opcodes.GETSTATIC,
className,
"methodInterceptor",
Type.getDescriptor(MethodInterceptor.class));

mv.visitFieldInsn(Opcodes.GETSTATIC,
className,
"methods",
Type.getDescriptor(Method[].class));
mv.visitLdcInsn(methodId);
mv.visitInsn(Opcodes.AALOAD);

mv.visitMethodInsn(Opcodes.INVOKEINTERFACE,
Type.getInternalName(MethodInterceptor.class),
"intercept",
Type.getMethodDescriptor(
Type.getType(boolean.class),
Type.getType(Method.class)),
true);
Label label = new Label();
mv.visitJumpInsn(Opcodes.IFEQ, label);
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitInsn(Opcodes.ARETURN);
mv.visitLabel(label);
mv.visitVarInsn(Opcodes.ALOAD, 0);
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < method.getParameterCount(); i++) {
mv.visitVarInsn(Type.getType(parameterTypes[i]).getOpcode(Opcodes.ILOAD), 1 + i);
}
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
superClassName,
method.getName(),
Type.getMethodDescriptor(method),
false);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}

byte[] classBytes = cv.toByteArray();

Class<?> cls = new ClassLoader() {
public Class<?> defineClass() {
return defineClass(className, classBytes, 0, classBytes.length);
}
}.defineClass();
Object instance;
try {
instance = cls
.getConstructor(constructorParameterTypes)
.newInstance(constructorArguments);
cls.getField("methods")
.set(instance, methods);
cls.getField("methodInterceptor")
.set(instance, interceptor);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
return instance;
}

}
@@ -0,0 +1,31 @@
/*
* SonarSource Language Recognizer
* Copyright (C) 2010-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sonar.sslr.impl.typed;

import java.lang.reflect.Method;

public interface MethodInterceptor {

/**
* @return {@code true} if original method should not be invoked
*/
boolean intercept(Method method);

}

0 comments on commit 3d0519b

Please sign in to comment.