org.mvel2.optimizers.impl.asm.ASMAccessorOptimizer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tbel Show documentation
Show all versions of tbel Show documentation
TBEL is a powerful expression language for ThingsBoard platform user-defined functions.
Original implementation is based on MVEL.
The newest version!
/**
* MVEL 2.0
* Copyright (C) 2007 The Codehaus
* Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor
*
* 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 org.mvel2.optimizers.impl.asm;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.mvel2.CompileException;
import org.mvel2.DataConversion;
import org.mvel2.ExecutionContext;
import org.mvel2.MVEL;
import org.mvel2.OptimizationFailure;
import org.mvel2.ParserContext;
import org.mvel2.PropertyAccessException;
import org.mvel2.asm.ClassWriter;
import org.mvel2.asm.Label;
import org.mvel2.asm.MethodVisitor;
import org.mvel2.asm.Opcodes;
import org.mvel2.ast.FunctionInstance;
import org.mvel2.ast.TypeDescriptor;
import org.mvel2.ast.WithNode;
import org.mvel2.compiler.Accessor;
import org.mvel2.compiler.AccessorNode;
import org.mvel2.compiler.ExecutableAccessor;
import org.mvel2.compiler.ExecutableLiteral;
import org.mvel2.compiler.ExecutableStatement;
import org.mvel2.compiler.PropertyVerifier;
import org.mvel2.integration.GlobalListenerFactory;
import org.mvel2.integration.PropertyHandler;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.optimizers.AbstractOptimizer;
import org.mvel2.optimizers.AccessorOptimizer;
import org.mvel2.optimizers.OptimizationNotSupported;
import org.mvel2.optimizers.impl.refl.nodes.Union;
import org.mvel2.util.JITClassLoader;
import org.mvel2.util.MVELClassLoader;
import org.mvel2.util.MethodStub;
import org.mvel2.util.NullType;
import org.mvel2.util.ParseTools;
import org.mvel2.util.PropertyTools;
import org.mvel2.util.StringAppender;
import static java.lang.String.valueOf;
import static java.lang.System.getProperty;
import static java.lang.Thread.currentThread;
import static java.lang.reflect.Array.getLength;
import static java.lang.reflect.Modifier.FINAL;
import static java.lang.reflect.Modifier.STATIC;
import static org.mvel2.DataConversion.canConvert;
import static org.mvel2.DataConversion.convert;
import static org.mvel2.MVEL.eval;
import static org.mvel2.MVEL.isAdvancedDebugging;
import static org.mvel2.asm.Opcodes.AALOAD;
import static org.mvel2.asm.Opcodes.AASTORE;
import static org.mvel2.asm.Opcodes.ACC_PRIVATE;
import static org.mvel2.asm.Opcodes.ACC_PUBLIC;
import static org.mvel2.asm.Opcodes.ACONST_NULL;
import static org.mvel2.asm.Opcodes.ALOAD;
import static org.mvel2.asm.Opcodes.ANEWARRAY;
import static org.mvel2.asm.Opcodes.ARETURN;
import static org.mvel2.asm.Opcodes.ARRAYLENGTH;
import static org.mvel2.asm.Opcodes.ASTORE;
import static org.mvel2.asm.Opcodes.ATHROW;
import static org.mvel2.asm.Opcodes.BALOAD;
import static org.mvel2.asm.Opcodes.BASTORE;
import static org.mvel2.asm.Opcodes.BIPUSH;
import static org.mvel2.asm.Opcodes.CALOAD;
import static org.mvel2.asm.Opcodes.CASTORE;
import static org.mvel2.asm.Opcodes.CHECKCAST;
import static org.mvel2.asm.Opcodes.DALOAD;
import static org.mvel2.asm.Opcodes.DASTORE;
import static org.mvel2.asm.Opcodes.DUP;
import static org.mvel2.asm.Opcodes.DUP_X1;
import static org.mvel2.asm.Opcodes.DUP_X2;
import static org.mvel2.asm.Opcodes.FALOAD;
import static org.mvel2.asm.Opcodes.FASTORE;
import static org.mvel2.asm.Opcodes.GETFIELD;
import static org.mvel2.asm.Opcodes.GETSTATIC;
import static org.mvel2.asm.Opcodes.GOTO;
import static org.mvel2.asm.Opcodes.IALOAD;
import static org.mvel2.asm.Opcodes.IASTORE;
import static org.mvel2.asm.Opcodes.ICONST_0;
import static org.mvel2.asm.Opcodes.ICONST_1;
import static org.mvel2.asm.Opcodes.ICONST_2;
import static org.mvel2.asm.Opcodes.ICONST_3;
import static org.mvel2.asm.Opcodes.ICONST_4;
import static org.mvel2.asm.Opcodes.ICONST_5;
import static org.mvel2.asm.Opcodes.IFEQ;
import static org.mvel2.asm.Opcodes.IFNONNULL;
import static org.mvel2.asm.Opcodes.IF_ICMPLT;
import static org.mvel2.asm.Opcodes.ILOAD;
import static org.mvel2.asm.Opcodes.INVOKEINTERFACE;
import static org.mvel2.asm.Opcodes.INVOKESPECIAL;
import static org.mvel2.asm.Opcodes.INVOKESTATIC;
import static org.mvel2.asm.Opcodes.INVOKEVIRTUAL;
import static org.mvel2.asm.Opcodes.ISTORE;
import static org.mvel2.asm.Opcodes.LALOAD;
import static org.mvel2.asm.Opcodes.LASTORE;
import static org.mvel2.asm.Opcodes.NEW;
import static org.mvel2.asm.Opcodes.NEWARRAY;
import static org.mvel2.asm.Opcodes.POP;
import static org.mvel2.asm.Opcodes.PUTFIELD;
import static org.mvel2.asm.Opcodes.RETURN;
import static org.mvel2.asm.Opcodes.SALOAD;
import static org.mvel2.asm.Opcodes.SASTORE;
import static org.mvel2.asm.Opcodes.SIPUSH;
import static org.mvel2.asm.Opcodes.SWAP;
import static org.mvel2.asm.Type.getConstructorDescriptor;
import static org.mvel2.asm.Type.getDescriptor;
import static org.mvel2.asm.Type.getInternalName;
import static org.mvel2.asm.Type.getMethodDescriptor;
import static org.mvel2.asm.Type.getType;
import static org.mvel2.ast.TypeDescriptor.getClassReference;
import static org.mvel2.integration.GlobalListenerFactory.hasGetListeners;
import static org.mvel2.integration.GlobalListenerFactory.notifyGetListeners;
import static org.mvel2.integration.PropertyHandlerFactory.getNullMethodHandler;
import static org.mvel2.integration.PropertyHandlerFactory.getNullPropertyHandler;
import static org.mvel2.integration.PropertyHandlerFactory.getPropertyHandler;
import static org.mvel2.integration.PropertyHandlerFactory.hasNullMethodHandler;
import static org.mvel2.integration.PropertyHandlerFactory.hasNullPropertyHandler;
import static org.mvel2.integration.PropertyHandlerFactory.hasPropertyHandler;
import static org.mvel2.util.ArrayTools.findFirst;
import static org.mvel2.util.ParseTools.EMPTY_OBJ_ARR;
import static org.mvel2.util.ParseTools.balancedCapture;
import static org.mvel2.util.ParseTools.balancedCaptureWithLineAccounting;
import static org.mvel2.util.ParseTools.captureContructorAndResidual;
import static org.mvel2.util.ParseTools.determineActualTargetMethod;
import static org.mvel2.util.ParseTools.findClass;
import static org.mvel2.util.ParseTools.getBaseComponentType;
import static org.mvel2.util.ParseTools.getBestCandidate;
import static org.mvel2.util.ParseTools.getBestConstructorCandidate;
import static org.mvel2.util.ParseTools.getSubComponentType;
import static org.mvel2.util.ParseTools.getWidenedTarget;
import static org.mvel2.util.ParseTools.isPrimitiveWrapper;
import static org.mvel2.util.ParseTools.parseMethodOrConstructor;
import static org.mvel2.util.ParseTools.parseParameterList;
import static org.mvel2.util.ParseTools.repeatChar;
import static org.mvel2.util.ParseTools.subCompileExpression;
import static org.mvel2.util.ParseTools.subset;
import static org.mvel2.util.PropertyTools.getFieldOrAccessor;
import static org.mvel2.util.PropertyTools.getFieldOrWriteAccessor;
import static org.mvel2.util.ReflectionUtil.toNonPrimitiveArray;
import static org.mvel2.util.ReflectionUtil.toNonPrimitiveType;
import static org.mvel2.util.Varargs.normalizeArgsForVarArgs;
import static org.mvel2.util.Varargs.paramTypeVarArgsSafe;
/**
* Implementation of the MVEL Just-in-Time (JIT) compiler for Property Accessors using the ASM bytecode
* engineering library.
*/
@SuppressWarnings({"TypeParameterExplicitlyExtendsObject", "unchecked", "UnusedDeclaration"})
public class ASMAccessorOptimizer extends AbstractOptimizer implements AccessorOptimizer {
private static final String MAP_IMPL = "java/util/HashMap";
private static String LIST_IMPL;
private static String NAMESPACE;
private static final int OPCODES_VERSION;
static {
final String javaVersion = PropertyTools.getJavaVersion();
if (javaVersion.startsWith("1.4")) {
OPCODES_VERSION = Opcodes.V1_4;
} else if (javaVersion.startsWith("1.5")) {
OPCODES_VERSION = Opcodes.V1_5;
} else {
OPCODES_VERSION = Opcodes.V1_6;
}
String defaultNameSapce = getProperty("mvel2.namespace");
if (defaultNameSapce == null) NAMESPACE = "org/mvel2/";
else NAMESPACE = defaultNameSapce;
String jitListImpl = getProperty("mvel2.jit.list_impl");
if (jitListImpl == null) LIST_IMPL = NAMESPACE + "util/FastList";
else LIST_IMPL = jitListImpl;
}
private Object ctx;
private Object thisRef;
private VariableResolverFactory variableFactory;
private static final Object[] EMPTYARG = new Object[0];
private static final Class[] EMPTYCLS = new Class[0];
private boolean first = true;
private boolean noinit = false;
private boolean deferFinish = false;
private boolean literal = false;
private boolean propNull = false;
private boolean methNull = false;
private String className;
private ClassWriter cw;
private MethodVisitor mv;
private Object val;
private int stacksize = 1;
private int maxlocals = 1;
private long time;
private ArrayList compiledInputs;
private Class ingressType;
private Class returnType;
private int compileDepth = 0;
@SuppressWarnings({"StringBufferField"})
private StringAppender buildLog;
public ASMAccessorOptimizer() {
//do this to confirm we're running the correct version
//otherwise should create a verification error in VM
new ClassWriter(ClassWriter.COMPUTE_MAXS);
}
private ASMAccessorOptimizer(ClassWriter cw, MethodVisitor mv,
ArrayList compiledInputs, String className,
StringAppender buildLog, int compileDepth) {
this.cw = cw;
this.mv = mv;
this.compiledInputs = compiledInputs;
this.className = className;
this.buildLog = buildLog;
this.compileDepth = compileDepth + 1;
noinit = true;
deferFinish = true;
}
/**
* Does all the boilerplate for initiating the JIT.
*/
private void _initJIT() {
if (isAdvancedDebugging()) {
buildLog = new StringAppender();
}
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
synchronized (Runtime.getRuntime()) {
cw.visit(OPCODES_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, className = "ASMAccessorImpl_"
+ valueOf(cw.hashCode()).replaceAll("\\-", "_") + (System.currentTimeMillis() / 10) +
((int) (Math.random() * 100)),
null, "java/lang/Object", new String[]{NAMESPACE + "compiler/Accessor"});
}
MethodVisitor m = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
m.visitCode();
m.visitVarInsn(Opcodes.ALOAD, 0);
m.visitMethodInsn(INVOKESPECIAL, "java/lang/Object",
"", "()V");
m.visitInsn(RETURN);
m.visitMaxs(1, 1);
m.visitEnd();
(mv = cw.visitMethod(ACC_PUBLIC, "getValue",
"(Ljava/lang/Object;Ljava/lang/Object;L" + NAMESPACE
+ "integration/VariableResolverFactory;)Ljava/lang/Object;", null, null)).visitCode();
}
private void _initJIT2() {
if (isAdvancedDebugging()) {
buildLog = new StringAppender();
}
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
synchronized (Runtime.getRuntime()) {
cw.visit(OPCODES_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, className = "ASMAccessorImpl_"
+ valueOf(cw.hashCode()).replaceAll("\\-", "_") + (System.currentTimeMillis() / 10) +
((int) (Math.random() * 100)),
null, "java/lang/Object", new String[]{NAMESPACE + "compiler/Accessor"});
}
MethodVisitor m = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
m.visitCode();
m.visitVarInsn(Opcodes.ALOAD, 0);
m.visitMethodInsn(INVOKESPECIAL, "java/lang/Object",
"", "()V");
m.visitInsn(RETURN);
m.visitMaxs(1, 1);
m.visitEnd();
(mv = cw.visitMethod(ACC_PUBLIC, "setValue",
"(Ljava/lang/Object;Ljava/lang/Object;L" + NAMESPACE
+ "integration/VariableResolverFactory;Ljava/lang/Object;)Ljava/lang/Object;", null, null)).visitCode();
}
public Accessor optimizeAccessor(ParserContext pCtx, char[] property, int start, int offset, Object staticContext,
Object thisRef, VariableResolverFactory factory, boolean root, Class ingressType) {
time = System.currentTimeMillis();
if (compiledInputs == null) compiledInputs = new ArrayList();
this.start = cursor = start;
this.end = start + offset;
this.length = end - this.start;
this.first = true;
this.val = null;
this.pCtx = pCtx;
this.expr = property;
this.ctx = staticContext;
this.thisRef = thisRef;
this.variableFactory = factory;
this.ingressType = ingressType;
if (!noinit) _initJIT();
return compileAccessor();
}
public Accessor optimizeSetAccessor(ParserContext pCtx, char[] property, int start, int offset, Object ctx,
Object thisRef, VariableResolverFactory factory, boolean rootThisRef,
Object value, Class ingressType) {
this.expr = property;
this.start = this.cursor = start;
this.end = start + offset;
this.length = start + offset;
this.first = true;
this.ingressType = ingressType;
compiledInputs = new ArrayList();
this.pCtx = pCtx;
this.ctx = ctx;
this.thisRef = thisRef;
this.variableFactory = factory;
char[] root = null;
PropertyVerifier verifier = new PropertyVerifier(property, this.pCtx = pCtx);
int split = findLastUnion();
if (split != -1) {
root = subset(property, 0, split);
}
AccessorNode rootAccessor = null;
_initJIT2();
if (root != null) {
int _length = this.length;
int _end = this.end;
char[] _expr = this.expr;
this.length = end = (this.expr = root).length;
// run the compiler but don't finish building.
deferFinish = true;
noinit = true;
compileAccessor();
ctx = this.val;
this.expr = _expr;
this.cursor = start + root.length + 1;
this.length = _length - root.length - 1;
this.end = this.cursor + this.length;
}
else {
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
}
try {
skipWhitespace();
if (collection) {
int st = cursor;
whiteSpaceSkip();
if (st == end)
throw new PropertyAccessException("unterminated '['", expr, start, pCtx);
if (scanTo(']'))
throw new PropertyAccessException("unterminated '['", expr, start, pCtx);
String ex = new String(expr, st, cursor - st).trim();
assert debug("CHECKCAST " + ctx.getClass().getName());
mv.visitTypeInsn(CHECKCAST, getInternalName(ctx.getClass()));
if (ctx instanceof Map) {
if (MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING && hasPropertyHandler(Map.class)) {
propHandlerByteCodePut(ex, ctx, Map.class, value);
}
else {
//noinspection unchecked
((Map) ctx).put(eval(ex, ctx, variableFactory), convert(value, returnType = verifier.analyze()));
writeLiteralOrSubexpression(subCompileExpression(ex.toCharArray(), pCtx));
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
if (value != null && returnType != value.getClass()) {
dataConversion(returnType);
checkcast(returnType);
}
assert debug("INVOKEINTERFACE Map.put");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
assert debug("POP");
mv.visitInsn(POP);
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
}
}
else if (ctx instanceof List) {
if (MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING && hasPropertyHandler(List.class)) {
propHandlerByteCodePut(ex, ctx, List.class, value);
}
else {
//noinspection unchecked
((List) ctx).set(eval(ex, ctx, variableFactory, Integer.class),
convert(value, returnType = verifier.analyze()));
writeLiteralOrSubexpression(subCompileExpression(ex.toCharArray(), pCtx));
unwrapPrimitive(int.class);
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
if (value != null && !value.getClass().isAssignableFrom(returnType)) {
dataConversion(returnType);
checkcast(returnType);
}
assert debug("INVOKEINTERFACE List.set");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "set", "(ILjava/lang/Object;)Ljava/lang/Object;");
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
}
}
else if (MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING && hasPropertyHandler(ctx.getClass())) {
propHandlerByteCodePut(ex, ctx, ctx.getClass(), value);
}
else if (ctx.getClass().isArray()) {
if (MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING && hasPropertyHandler(Array.class)) {
propHandlerByteCodePut(ex, ctx, Array.class, value);
}
else {
Class type = getBaseComponentType(ctx.getClass());
Object idx = eval(ex, ctx, variableFactory);
writeLiteralOrSubexpression(subCompileExpression(ex.toCharArray(), pCtx), int.class);
if (!(idx instanceof Integer)) {
dataConversion(Integer.class);
idx = DataConversion.convert(idx, Integer.class);
unwrapPrimitive(int.class);
}
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
if (type.isPrimitive()) unwrapPrimitive(type);
else if (!type.equals(value.getClass())) {
dataConversion(type);
}
arrayStore(type);
//noinspection unchecked
Array.set(ctx, (Integer) idx, convert(value, type));
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
}
}
else {
throw new PropertyAccessException("cannot bind to collection property: " + new String(expr)
+ ": not a recognized collection type: " + ctx.getClass(), expr, start, pCtx);
}
deferFinish = false;
noinit = false;
_finishJIT();
try {
deferFinish = false;
return _initializeAccessor();
}
catch (Exception e) {
throw new CompileException("could not generate accessor", expr, start, e);
}
}
String tk = new String(expr, this.cursor, end - cursor);
Member member = getFieldOrWriteAccessor(ctx.getClass(), tk, value == null ? null : ingressType);
if (GlobalListenerFactory.hasSetListeners()) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(tk);
mv.visitVarInsn(ALOAD, 3);
mv.visitVarInsn(ALOAD, 4);
mv.visitMethodInsn(INVOKESTATIC, NAMESPACE + "integration/GlobalListenerFactory",
"notifySetListeners", "(Ljava/lang/Object;Ljava/lang/String;L" + NAMESPACE
+ "integration/VariableResolverFactory;Ljava/lang/Object;)V");
GlobalListenerFactory.notifySetListeners(ctx, tk, variableFactory, value);
}
if (member instanceof Field) {
checkcast(ctx.getClass());
Field fld = (Field) member;
Label jmp = null;
Label jmp2 = new Label();
if (fld.getType().isPrimitive()) {
assert debug("ASTORE 5");
mv.visitVarInsn(ASTORE, 5);
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
if (value == null) value = PropertyTools.getPrimitiveInitialValue(fld.getType());
jmp = new Label();
assert debug("IFNOTNULL jmp");
mv.visitJumpInsn(IFNONNULL, jmp);
assert debug("ALOAD 5");
mv.visitVarInsn(ALOAD, 5);
assert debug("ICONST_0");
mv.visitInsn(ICONST_0);
assert debug("PUTFIELD " + getInternalName(fld.getDeclaringClass()) + "." + tk);
mv.visitFieldInsn(PUTFIELD, getInternalName(fld.getDeclaringClass()), tk, getDescriptor(fld.getType()));
assert debug("GOTO jmp2");
mv.visitJumpInsn(GOTO, jmp2);
assert debug("jmp:");
mv.visitLabel(jmp);
assert debug("ALOAD 5");
mv.visitVarInsn(ALOAD, 5);
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
unwrapPrimitive(fld.getType());
}
else {
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
checkcast(fld.getType());
}
if (jmp == null && value != null && !fld.getType().isAssignableFrom(value.getClass())) {
if (!canConvert(fld.getType(), value.getClass())) {
throw new CompileException("cannot convert type: "
+ value.getClass() + ": to " + fld.getType(), expr, start);
}
dataConversion(fld.getType());
fld.set(ctx, convert(value, fld.getType()));
}
else {
fld.set(ctx, value);
}
assert debug("PUTFIELD " + getInternalName(fld.getDeclaringClass()) + "." + tk);
mv.visitFieldInsn(PUTFIELD, getInternalName(fld.getDeclaringClass()), tk, getDescriptor(fld.getType()));
assert debug("jmp2:");
mv.visitLabel(jmp2);
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
}
else if (member != null) {
assert debug("CHECKCAST " + getInternalName(ctx.getClass()));
mv.visitTypeInsn(CHECKCAST, getInternalName(ctx.getClass()));
Method meth = (Method) member;
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
Class targetType = meth.getParameterTypes()[0];
Label jmp;
Label jmp2 = new Label();
if (value != null && !targetType.isAssignableFrom(value.getClass())) {
if (!canConvert(targetType, value.getClass())) {
throw new CompileException("cannot convert type: "
+ value.getClass() + ": to " + meth.getParameterTypes()[0], expr, start);
}
dataConversion(getWrapperClass(targetType));
if (targetType.isPrimitive()) {
unwrapPrimitive(targetType);
}
else checkcast(targetType);
meth.invoke(ctx, convert(value, meth.getParameterTypes()[0]));
}
else {
if (targetType.isPrimitive()) {
if (value == null) value = PropertyTools.getPrimitiveInitialValue(targetType);
jmp = new Label();
assert debug("IFNOTNULL jmp");
mv.visitJumpInsn(IFNONNULL, jmp);
assert debug("ICONST_0");
mv.visitInsn(ICONST_0);
assert debug("INVOKEVIRTUAL " + getInternalName(meth.getDeclaringClass()) + "." + meth.getName());
mv.visitMethodInsn(INVOKEVIRTUAL, getInternalName(meth.getDeclaringClass()), meth.getName(),
getMethodDescriptor(meth));
assert debug("GOTO jmp2");
mv.visitJumpInsn(GOTO, jmp2);
assert debug("jmp:");
mv.visitLabel(jmp);
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
unwrapPrimitive(targetType);
}
else {
checkcast(targetType);
}
meth.invoke(ctx, value);
}
assert debug("INVOKEVIRTUAL " + getInternalName(meth.getDeclaringClass()) + "." + meth.getName());
mv.visitMethodInsn(INVOKEVIRTUAL, getInternalName(meth.getDeclaringClass()), meth.getName(),
getMethodDescriptor(meth));
assert debug("jmp2:");
mv.visitLabel(jmp2);
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
}
else if (ctx instanceof Map) {
assert debug("CHECKCAST " + getInternalName(ctx.getClass()));
mv.visitTypeInsn(CHECKCAST, getInternalName(ctx.getClass()));
assert debug("LDC '" + tk + "'");
mv.visitLdcInsn(tk);
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
assert debug("INVOKEINTERFACE java/util/Map.put");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
//noinspection unchecked
((Map) ctx).put(tk, value);
}
else {
throw new PropertyAccessException("could not access property (" + tk + ") in: "
+ ingressType.getName(), expr, start, pCtx);
}
}
catch (InvocationTargetException e) {
throw new PropertyAccessException("could not access property", expr, start, e, pCtx);
}
catch (IllegalAccessException e) {
throw new PropertyAccessException("could not access property", expr, start, e, pCtx);
}
try {
deferFinish = false;
noinit = false;
_finishJIT();
return _initializeAccessor();
}
catch (Exception e) {
throw new CompileException("could not generate accessor", expr, start, e);
}
}
private void _finishJIT() {
if (deferFinish) {
return;
}
if (returnType != null && returnType.isPrimitive()) {
//noinspection unchecked
wrapPrimitive(returnType);
}
if (returnType == void.class) {
assert debug("ACONST_NULL");
mv.visitInsn(ACONST_NULL);
}
assert debug("ARETURN");
mv.visitInsn(ARETURN);
assert debug("\n{METHOD STATS (maxstack=" + stacksize + ")}\n");
dumpAdvancedDebugging(); // dump advanced debugging if necessary
mv.visitMaxs(stacksize, maxlocals);
mv.visitEnd();
mv = cw.visitMethod(ACC_PUBLIC, "getKnownEgressType", "()Ljava/lang/Class;", null, null);
mv.visitCode();
visitConstantClass(returnType);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
if (propNull) {
cw.visitField(ACC_PUBLIC, "nullPropertyHandler", "L" + NAMESPACE + "integration/PropertyHandler;", null, null).visitEnd();
}
if (methNull) {
cw.visitField(ACC_PUBLIC, "nullMethodHandler", "L" + NAMESPACE + "integration/PropertyHandler;", null, null).visitEnd();
}
buildInputs();
if (buildLog != null && buildLog.length() != 0 && expr != null) {
mv = cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLdcInsn(buildLog.toString() + "\n\n## { " + new String(expr) + " }");
mv.visitInsn(ARETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
cw.visitEnd();
}
private void visitConstantClass(Class> clazz) {
if (clazz == null) clazz = Object.class;
if (clazz.isPrimitive()) {
mv.visitFieldInsn(GETSTATIC, toNonPrimitiveType(clazz).getName().replace(".", "/"), "TYPE", "Ljava/lang/Class;");
} else {
mv.visitLdcInsn(org.mvel2.asm.Type.getType(clazz));
}
}
private Accessor _initializeAccessor() throws Exception {
if (deferFinish) {
return null;
}
/**
* Hot load the class we just generated.
*/
Class cls = loadClass(className, cw.toByteArray());
assert debug("[MVEL JIT Completed Optimization <<" + (expr != null ? new String(expr) : "") + ">>]::" + cls
+ " (time: " + (System.currentTimeMillis() - time) + "ms)");
Object o;
try {
if (compiledInputs.size() == 0) {
o = cls.newInstance();
}
else {
Class[] parms = new Class[compiledInputs.size()];
for (int i = 0; i < compiledInputs.size(); i++) {
parms[i] = ExecutableStatement.class;
}
o = cls.getConstructor(parms).newInstance(compiledInputs.toArray(new ExecutableStatement[compiledInputs.size()]));
}
if (propNull) cls.getField("nullPropertyHandler").set(o, getNullPropertyHandler());
if (methNull) cls.getField("nullMethodHandler").set(o, getNullMethodHandler());
}
catch (VerifyError e) {
System.out.println("**** COMPILER BUG! REPORT THIS IMMEDIATELY AT http://jira.codehaus.org/browse/MVEL");
System.out.println("Expression: " + (expr == null ? null : new String(expr)));
throw e;
}
return (Accessor) o;
}
private Accessor compileAccessor() {
assert debug("<>");
Object curr = ctx instanceof ExecutionContext ? null : ctx;
try {
if (!MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING) {
while (cursor < end) {
switch (nextSubToken()) {
case BEAN:
curr = getBeanProperty(curr, capture());
break;
case METH:
curr = getMethod(curr, capture());
break;
case COL:
curr = getCollectionProperty(curr, capture());
break;
case WITH:
curr = getWithProperty(curr);
break;
}
// check to see if a null safety is enabled on this property.
if (fields == -1) {
if (curr == null) {
if (nullSafe) {
throw new OptimizationNotSupported();
}
break;
}
else {
fields = 0;
}
}
first = false;
if (nullSafe && cursor < end) {
assert debug("DUP");
mv.visitInsn(DUP);
Label j = new Label();
assert debug("IFNONNULL : jump");
mv.visitJumpInsn(IFNONNULL, j);
assert debug("ARETURN");
mv.visitInsn(ARETURN);
assert debug("LABEL:jump");
mv.visitLabel(j);
}
}
}
else {
while (cursor < end) {
switch (nextSubToken()) {
case BEAN:
curr = getBeanPropertyAO(curr, capture());
break;
case METH:
curr = getMethod(curr, capture());
break;
case COL:
curr = getCollectionPropertyAO(curr, capture());
break;
case WITH:
curr = getWithProperty(curr);
break;
}
// check to see if a null safety is enabled on this property.
if (fields == -1) {
if (curr == null) {
if (nullSafe) {
throw new OptimizationNotSupported();
}
break;
}
else {
fields = 0;
}
}
first = false;
if (nullSafe && cursor < end) {
assert debug("DUP");
mv.visitInsn(DUP);
Label j = new Label();
assert debug("IFNONNULL : jump");
mv.visitJumpInsn(IFNONNULL, j);
assert debug("ARETURN");
mv.visitInsn(ARETURN);
assert debug("LABEL:jump");
mv.visitLabel(j);
}
}
}
val = curr;
_finishJIT();
return _initializeAccessor();
}
catch (InvocationTargetException e) {
throw new PropertyAccessException(new String(expr), expr, st, e, pCtx);
}
catch (IllegalAccessException e) {
throw new PropertyAccessException(new String(expr), expr, st, e, pCtx);
}
catch (IndexOutOfBoundsException e) {
throw new PropertyAccessException(new String(expr), expr, st, e, pCtx);
}
catch (PropertyAccessException e) {
throw new CompileException(e.getMessage(), expr, st, e);
}
catch (CompileException e) {
throw e;
}
catch (NullPointerException e) {
throw new PropertyAccessException(new String(expr), expr, st, e, pCtx);
}
catch (OptimizationNotSupported e) {
throw e;
}
catch (Exception e) {
throw new CompileException(e.getMessage(), expr, st, e);
}
}
private Object getWithProperty(Object ctx) {
assert debug("\n ** ENTER -> {with}");
if (first) {
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
first = false;
}
String root = new String(expr, 0, cursor - 1).trim();
int start = cursor + 1;
cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '{', pCtx);
this.returnType = ctx != null ? ctx.getClass() : null;
for (WithNode.ParmValuePair aPvp : WithNode.compileWithExpressions(expr, start, cursor++ - start, root, ingressType, pCtx)) {
assert debug("DUP");
mv.visitInsn(DUP);
assert debug("ASTORE " + (5 + compileDepth) + " (withctx)");
mv.visitVarInsn(ASTORE, 5 + compileDepth);
aPvp.eval(ctx, variableFactory);
if (aPvp.getSetExpression() == null) {
addSubstatement(aPvp.getStatement());
}
else {
compiledInputs.add((ExecutableStatement) aPvp.getSetExpression());
// load set expression
assert debug("ALOAD 0");
mv.visitVarInsn(ALOAD, 0);
assert debug("GETFIELD p" + (compiledInputs.size() - 1));
mv.visitFieldInsn(GETFIELD, className, "p" + (compiledInputs.size() - 1), "L" + NAMESPACE
+ "compiler/ExecutableStatement;");
// ctx
assert debug("ALOAD " + (5 + compileDepth) + "(withctx)");
mv.visitVarInsn(ALOAD, (5 + compileDepth));
// elCtx
assert debug("ALOAD 2");
mv.visitVarInsn(ALOAD, 2);
// variable factory
assert debug("ALOAD 3");
mv.visitVarInsn(ALOAD, 3);
// the value to set.
addSubstatement(aPvp.getStatement());
assert debug("INVOKEINTERFACE Accessor.setValue");
mv.visitMethodInsn(INVOKEINTERFACE, NAMESPACE + "compiler/ExecutableStatement",
"setValue",
"(Ljava/lang/Object;Ljava/lang/Object;L"
+ NAMESPACE + "integration/VariableResolverFactory;Ljava/lang/Object;)Ljava/lang/Object;");
assert debug("POP");
mv.visitInsn(POP);
}
}
return ctx;
}
private Object getBeanPropertyAO(Object ctx, String property)
throws IllegalAccessException, InvocationTargetException {
if (ctx != null && hasPropertyHandler(ctx.getClass())) {
return propHandlerByteCode(property, ctx, ctx.getClass());
}
return getBeanProperty(ctx, property);
}
private Object getBeanProperty(Object ctx, String property)
throws IllegalAccessException, InvocationTargetException {
assert debug("\n ** ENTER -> {bean: " + property + "; ctx=" + ctx + "}");
if ((pCtx == null ? currType : pCtx.getVarOrInputTypeOrNull(property)) == Object.class
&& !pCtx.isStrongTyping()) {
currType = null;
}
if (returnType != null && returnType.isPrimitive()) {
//noinspection unchecked
wrapPrimitive(returnType);
}
boolean classRef = false;
Class> cls;
if (ctx instanceof Class) {
if (MVEL.COMPILER_OPT_SUPPORT_JAVA_STYLE_CLASS_LITERALS
&& "class".equals(property)) {
ldcClassConstant((Class>) ctx);
return ctx;
}
cls = (Class>) ctx;
classRef = true;
}
else if (ctx != null) {
cls = ctx.getClass();
}
else {
cls = null;
}
if (hasPropertyHandler(cls)) {
PropertyHandler prop = getPropertyHandler(cls);
if (prop instanceof ProducesBytecode) {
((ProducesBytecode) prop).produceBytecodeGet(mv, property, variableFactory);
return prop.getProperty(property, ctx, variableFactory);
}
else {
throw new RuntimeException("unable to compileShared: custom accessor does not support producing bytecode: "
+ prop.getClass().getName());
}
}
Member member = cls != null ? getFieldOrAccessor(cls, property) : null;
if (member != null && classRef && (member.getModifiers() & Modifier.STATIC) == 0) {
member = null;
}
if (member != null && hasGetListeners()) {
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(member.getName());
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKESTATIC, NAMESPACE + "integration/GlobalListenerFactory", "notifyGetListeners",
"(Ljava/lang/Object;Ljava/lang/String;L" + NAMESPACE + "integration/VariableResolverFactory;)V");
notifyGetListeners(ctx, member.getName(), variableFactory);
}
if (first) {
if ("this".equals(property)) {
assert debug("ALOAD 2");
mv.visitVarInsn(ALOAD, 2);
return thisRef;
}
else if (variableFactory != null && variableFactory.isResolveable(property)) {
if (variableFactory.isIndexedFactory() && variableFactory.isTarget(property)) {
int idx;
try {
loadVariableByIndex(idx = variableFactory.variableIndexOf(property));
}
catch (Exception e) {
throw new OptimizationFailure(property);
}
return variableFactory.getIndexedVariableResolver(idx).getValue();
}
else {
try {
loadVariableByName(property);
}
catch (Exception e) {
throw new OptimizationFailure("critical error in JIT", e);
}
return variableFactory.getVariableResolver(property).getValue();
}
}
else {
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
}
}
if (member instanceof Field) {
return optimizeFieldMethodProperty(ctx, property, cls, member);
}
else if (member != null) {
Object o;
if (first) {
assert debug("ALOAD 1 (B)");
mv.visitVarInsn(ALOAD, 1);
}
try {
o = ((Method) member).invoke(ctx, EMPTYARG);
if (returnType != member.getDeclaringClass()) {
assert debug("CHECKCAST " + getInternalName(member.getDeclaringClass()));
mv.visitTypeInsn(CHECKCAST, getInternalName(member.getDeclaringClass()));
}
returnType = ((Method) member).getReturnType();
if (member.getDeclaringClass().isInterface()) {
assert debug("INVOKEINTERFACE " + member.getName() + ":" + returnType);
mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(member.getDeclaringClass()), member.getName(),
getMethodDescriptor((Method) member));
}
else {
assert debug("INVOKEVIRTUAL " + member.getName() + ":" + returnType);
mv.visitMethodInsn(INVOKEVIRTUAL, getInternalName(member.getDeclaringClass()), member.getName(),
getMethodDescriptor((Method) member));
}
}
catch (IllegalAccessException e) {
Method iFaceMeth = determineActualTargetMethod((Method) member);
if (iFaceMeth == null)
throw new PropertyAccessException("could not access field: " + cls.getName() + "." + property, expr, st, e, pCtx);
assert debug("CHECKCAST " + getInternalName(iFaceMeth.getDeclaringClass()));
mv.visitTypeInsn(CHECKCAST, getInternalName(iFaceMeth.getDeclaringClass()));
returnType = iFaceMeth.getReturnType();
assert debug("INVOKEINTERFACE " + member.getName() + ":" + returnType);
mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(iFaceMeth.getDeclaringClass()), member.getName(),
getMethodDescriptor((Method) member));
o = iFaceMeth.invoke(ctx, EMPTYARG);
}
catch (IllegalArgumentException e) {
if (member.getDeclaringClass().equals(ctx)) {
try {
Class c = Class.forName(member.getDeclaringClass().getName() + "$" + property);
throw new CompileException("name collision between innerclass: " + c.getCanonicalName()
+ "; and bean accessor: " + property + " (" + member.toString() + ")", expr, tkStart);
}
catch (ClassNotFoundException e2) {
//fallthru
}
}
throw e;
}
if (hasNullPropertyHandler()) {
if (o == null) o = getNullPropertyHandler().getProperty(member.getName(), ctx, variableFactory);
writeOutNullHandler(member, 0);
}
currType = toNonPrimitiveType(returnType);
return o;
}
else if (ctx instanceof Map && (((Map) ctx).containsKey(property) || nullSafe)) {
assert debug("CHECKCAST java/util/Map");
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
assert debug("LDC: \"" + property + "\"");
mv.visitLdcInsn(property);
assert debug("INVOKEINTERFACE: get");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
return ((Map) ctx).get(property);
}
else if (first && "this".equals(property)) {
assert debug("ALOAD 2");
mv.visitVarInsn(ALOAD, 2); // load the thisRef value.
return this.thisRef;
}
else if ("length".equals(property) && ctx.getClass().isArray()) {
anyArrayCheck(ctx.getClass());
assert debug("ARRAYLENGTH");
mv.visitInsn(ARRAYLENGTH);
wrapPrimitive(int.class);
return getLength(ctx);
}
else if (pCtx != null && pCtx.hasLiteral(property) || pCtx == null && LITERALS.containsKey(property)) {
Object lit = pCtx != null ? pCtx.getLiteral(property) : LITERALS.get(property);
if (lit instanceof Class) {
ldcClassConstant((Class) lit);
}
return lit;
}
else {
Object ts = tryStaticAccess();
if (ts != null) {
if (ts instanceof Class) {
ldcClassConstant((Class) ts);
return ts;
}
else if (ts instanceof Method) {
writeFunctionPointerStub(((Method) ts).getDeclaringClass(), (Method) ts);
return ts;
}
else {
Field f = (Field) ts;
return optimizeFieldMethodProperty(ctx, property, cls, f);
}
}
else if (ctx instanceof Class) {
/**
* This is our ugly support for function pointers. This works but needs to be re-thought out at some
* point.
*/
Class c = (Class) ctx;
for (Method m : c.getMethods()) {
if (property.equals(m.getName())) {
if (pCtx!=null&& pCtx.getParserConfiguration()!=null?pCtx.getParserConfiguration().isAllowNakedMethCall():MVEL.COMPILER_OPT_ALLOW_NAKED_METH_CALL) {
assert debug("POP");
mv.visitInsn(POP);
assert debug("INVOKESTATIC " + m.getName());
mv.visitMethodInsn(INVOKESTATIC, getInternalName(m.getDeclaringClass()), m.getName(),
getMethodDescriptor(m));
returnType = m.getReturnType();
return m.invoke(null, EMPTY_OBJ_ARR);
}
else {
writeFunctionPointerStub(c, m);
return m;
}
}
}
try {
Class subClass = findClass(variableFactory, c.getName() + "$" + property, pCtx);
ldcClassConstant(subClass);
return subClass;
}
catch (ClassNotFoundException cnfe) {
// fall through.
}
}
else if (pCtx!=null&& pCtx.getParserConfiguration()!=null?pCtx.getParserConfiguration().isAllowNakedMethCall():MVEL.COMPILER_OPT_ALLOW_NAKED_METH_CALL) {
return getMethod(ctx, property);
}
if (ctx == null) {
throw new PropertyAccessException("unresolvable property or identifier: " + property, expr, st, pCtx);
} else {
if (ctx instanceof Map) {
assert debug("CHECKCAST java/util/Map");
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
assert debug("LDC: \"" + property + "\"");
mv.visitLdcInsn(property);
assert debug("INVOKEINTERFACE: get");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
return ((Map) ctx).get(property);
} else {
throw new PropertyAccessException("could not access: " + property + "; in class: "
+ ctx.getClass().getName(), expr, st, pCtx);
}
}
}
}
private Object optimizeFieldMethodProperty(Object ctx, String property, Class> cls, Member member)
throws IllegalAccessException
{
Object o = ((Field) member).get(ctx);
if (((member.getModifiers() & STATIC) != 0)) {
// Check if the static field reference is a constant and a primitive.
if ((member.getModifiers() & FINAL) != 0 && (o instanceof String || ((Field) member).getType().isPrimitive())) {
o = ((Field) member).get(null);
assert debug("LDC " + valueOf(o));
mv.visitLdcInsn(o);
wrapPrimitive(o.getClass());
if (hasNullPropertyHandler()) {
if (o == null) {
o = getNullPropertyHandler().getProperty(member.getName(), ctx, variableFactory);
}
writeOutNullHandler(member, 0);
}
return o;
}
else {
assert debug("GETSTATIC " + getDescriptor(member.getDeclaringClass()) + "."
+ member.getName() + "::" + getDescriptor(((Field) member).getType()));
mv.visitFieldInsn(GETSTATIC, getInternalName(member.getDeclaringClass()),
member.getName(), getDescriptor(returnType = ((Field) member).getType()));
}
}
else {
assert debug("CHECKCAST " + getInternalName(cls));
mv.visitTypeInsn(CHECKCAST, getInternalName(cls));
assert debug("GETFIELD " + property + ":" + getDescriptor(((Field) member).getType()));
mv.visitFieldInsn(GETFIELD, getInternalName(cls), property, getDescriptor(returnType = ((Field) member)
.getType()));
}
returnType = ((Field) member).getType();
if (hasNullPropertyHandler()) {
if (o == null) {
o = getNullPropertyHandler().getProperty(member.getName(), ctx, variableFactory);
}
writeOutNullHandler(member, 0);
}
currType = toNonPrimitiveType(returnType);
return o;
}
private void writeFunctionPointerStub(Class c, Method m) {
ldcClassConstant(c);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;");
mv.visitVarInsn(ASTORE, 7);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ISTORE, 5);
mv.visitVarInsn(ALOAD, 7);
mv.visitInsn(ARRAYLENGTH);
mv.visitVarInsn(ISTORE, 6);
Label l1 = new Label();
mv.visitJumpInsn(GOTO, l1);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitVarInsn(ALOAD, 7);
mv.visitVarInsn(ILOAD, 5);
mv.visitInsn(AALOAD);
mv.visitVarInsn(ASTORE, 4);
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitLdcInsn(m.getName());
mv.visitVarInsn(ALOAD, 4);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "getName", "()Ljava/lang/String;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z");
Label l4 = new Label();
mv.visitJumpInsn(IFEQ, l4);
Label l5 = new Label();
mv.visitLabel(l5);
mv.visitVarInsn(ALOAD, 4);
mv.visitInsn(ARETURN);
mv.visitLabel(l4);
mv.visitIincInsn(5, 1);
mv.visitLabel(l1);
mv.visitVarInsn(ILOAD, 5);
mv.visitVarInsn(ILOAD, 6);
mv.visitJumpInsn(IF_ICMPLT, l2);
Label l6 = new Label();
mv.visitLabel(l6);
mv.visitInsn(ACONST_NULL);
mv.visitInsn(ARETURN);
// deferFinish = true;
}
private Object getCollectionProperty(Object ctx, String prop)
throws IllegalAccessException, InvocationTargetException {
if (prop.trim().length() > 0) {
ctx = getBeanProperty(ctx, prop);
first = false;
}
currType = null;
assert debug("\n ** ENTER -> {collection:<<" + prop + ">>; ctx=" + ctx + "}");
int start = ++cursor;
skipWhitespace();
if (cursor == end)
throw new CompileException("unterminated '['", expr, st);
if (scanTo(']'))
throw new CompileException("unterminated '['", expr, st);
String tk = new String(expr, start, cursor - start);
assert debug("{collection token: [" + tk + "]}");
if (ctx == null) return null;
if (first) {
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
}
ExecutableStatement compiled = (ExecutableStatement) subCompileExpression(tk.toCharArray(), pCtx);
Object item = compiled.getValue(this.ctx, variableFactory);
++cursor;
if (ctx instanceof Map) {
assert debug("CHECKCAST java/util/Map");
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
Class c = writeLiteralOrSubexpression(compiled);
if (c != null && c.isPrimitive()) {
wrapPrimitive(c);
}
assert debug("INVOKEINTERFACE: get");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
return ((Map) ctx).get(item);
}
else if (ctx instanceof List) {
assert debug("CHECKCAST java/util/List");
mv.visitTypeInsn(CHECKCAST, "java/util/List");
writeLiteralOrSubexpression(compiled, int.class);
assert debug("INVOKEINTERFACE: java/util/List.get");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;");
return ((List) ctx).get(convert(item, Integer.class));
}
else if (ctx.getClass().isArray()) {
assert debug("CHECKCAST " + getDescriptor(ctx.getClass()));
mv.visitTypeInsn(CHECKCAST, getDescriptor(ctx.getClass()));
writeLiteralOrSubexpression(compiled, int.class, item.getClass());
Class cls = getBaseComponentType(ctx.getClass());
if (cls.isPrimitive()) {
if (cls == int.class) {
assert debug("IALOAD");
mv.visitInsn(IALOAD);
}
else if (cls == char.class) {
assert debug("CALOAD");
mv.visitInsn(CALOAD);
}
else if (cls == boolean.class) {
assert debug("BALOAD");
mv.visitInsn(BALOAD);
}
else if (cls == double.class) {
assert debug("DALOAD");
mv.visitInsn(DALOAD);
}
else if (cls == float.class) {
assert debug("FALOAD");
mv.visitInsn(FALOAD);
}
else if (cls == short.class) {
assert debug("SALOAD");
mv.visitInsn(SALOAD);
}
else if (cls == long.class) {
assert debug("LALOAD");
mv.visitInsn(LALOAD);
}
else if (cls == byte.class) {
assert debug("BALOAD");
mv.visitInsn(BALOAD);
}
wrapPrimitive(cls);
}
else {
assert debug("AALOAD");
mv.visitInsn(AALOAD);
}
return Array.get(ctx, convert(item, Integer.class));
}
else if (ctx instanceof CharSequence) {
assert debug("CHECKCAST java/lang/CharSequence");
mv.visitTypeInsn(CHECKCAST, "java/lang/CharSequence");
if (item instanceof Integer) {
intPush((Integer) item);
assert debug("INVOKEINTERFACE java/lang/CharSequence.charAt");
mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/CharSequence", "charAt", "(I)C");
wrapPrimitive(char.class);
return ((CharSequence) ctx).charAt((Integer) item);
}
else {
writeLiteralOrSubexpression(compiled, Integer.class);
unwrapPrimitive(int.class);
assert debug("INVOKEINTERFACE java/lang/CharSequence.charAt");
mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/CharSequence", "charAt", "(I)C");
wrapPrimitive(char.class);
return ((CharSequence) ctx).charAt(convert(item, Integer.class));
}
}
else {
TypeDescriptor tDescr = new TypeDescriptor(expr, this.start, length, 0);
if (tDescr.isArray()) {
try {
Class cls = getClassReference((Class) ctx, tDescr, variableFactory, pCtx);
// rootNode = new StaticReferenceAccessor(cls);
ldcClassConstant(cls);
return cls;
}
catch (Exception e) {
//fall through
}
}
throw new CompileException("illegal use of []: unknown type: "
+ (ctx == null ? null : ctx.getClass().getName()), expr, st);
}
}
private Object getCollectionPropertyAO(Object ctx, String prop)
throws IllegalAccessException, InvocationTargetException {
if (prop.length() > 0) {
ctx = getBeanProperty(ctx, prop);
first = false;
}
currType = null;
assert debug("\n ** ENTER -> {collection:<<" + prop + ">>; ctx=" + ctx + "}");
int _start = ++cursor;
skipWhitespace();
if (cursor == end)
throw new CompileException("unterminated '['", expr, st);
if (scanTo(']'))
throw new CompileException("unterminated '['", expr, st);
String tk = new String(expr, _start, cursor - _start);
assert debug("{collection token:<<" + tk + ">>}");
if (ctx == null) return null;
ExecutableStatement compiled = (ExecutableStatement) subCompileExpression(tk.toCharArray());
Object item = compiled.getValue(this.ctx, variableFactory);
++cursor;
if (ctx instanceof Map) {
if (hasPropertyHandler(Map.class)) {
return propHandlerByteCode(tk, ctx, Map.class);
}
else {
if (first) {
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
}
assert debug("CHECKCAST java/util/Map");
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
Class c = writeLiteralOrSubexpression(compiled);
if (c != null && c.isPrimitive()) {
wrapPrimitive(c);
}
assert debug("INVOKEINTERFACE: Map.get");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
}
return ((Map) ctx).get(item);
}
else if (ctx instanceof List) {
if (hasPropertyHandler(List.class)) {
return propHandlerByteCode(tk, ctx, List.class);
}
else {
if (first) {
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
}
assert debug("CHECKCAST java/util/List");
mv.visitTypeInsn(CHECKCAST, "java/util/List");
writeLiteralOrSubexpression(compiled, int.class);
assert debug("INVOKEINTERFACE: java/util/List.get");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;");
return ((List) ctx).get(convert(item, Integer.class));
}
}
else if (ctx.getClass().isArray()) {
if (hasPropertyHandler(Array.class)) {
return propHandlerByteCode(tk, ctx, Array.class);
}
else {
if (first) {
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
}
assert debug("CHECKCAST " + getDescriptor(ctx.getClass()));
mv.visitTypeInsn(CHECKCAST, getDescriptor(ctx.getClass()));
writeLiteralOrSubexpression(compiled, int.class, item.getClass());
Class cls = getBaseComponentType(ctx.getClass());
if (cls.isPrimitive()) {
if (cls == int.class) {
assert debug("IALOAD");
mv.visitInsn(IALOAD);
}
else if (cls == char.class) {
assert debug("CALOAD");
mv.visitInsn(CALOAD);
}
else if (cls == boolean.class) {
assert debug("BALOAD");
mv.visitInsn(BALOAD);
}
else if (cls == double.class) {
assert debug("DALOAD");
mv.visitInsn(DALOAD);
}
else if (cls == float.class) {
assert debug("FALOAD");
mv.visitInsn(FALOAD);
}
else if (cls == short.class) {
assert debug("SALOAD");
mv.visitInsn(SALOAD);
}
else if (cls == long.class) {
assert debug("LALOAD");
mv.visitInsn(LALOAD);
}
else if (cls == byte.class) {
assert debug("BALOAD");
mv.visitInsn(BALOAD);
}
wrapPrimitive(cls);
}
else {
assert debug("AALOAD");
mv.visitInsn(AALOAD);
}
return Array.get(ctx, convert(item, Integer.class));
}
}
else if (ctx instanceof CharSequence) {
if (hasPropertyHandler(CharSequence.class)) {
return propHandlerByteCode(tk, ctx, CharSequence.class);
}
else {
if (first) {
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
}
assert debug("CHECKCAST java/lang/CharSequence");
mv.visitTypeInsn(CHECKCAST, "java/lang/CharSequence");
if (item instanceof Integer) {
intPush((Integer) item);
assert debug("INVOKEINTERFACE java/lang/CharSequence.charAt");
mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/CharSequence", "charAt", "(I)C");
wrapPrimitive(char.class);
return ((CharSequence) ctx).charAt((Integer) item);
}
else {
writeLiteralOrSubexpression(compiled, Integer.class);
unwrapPrimitive(int.class);
assert debug("INVOKEINTERFACE java/lang/CharSequence.charAt");
mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/CharSequence", "charAt", "(I)C");
wrapPrimitive(char.class);
return ((CharSequence) ctx).charAt(convert(item, Integer.class));
}
}
}
else {
TypeDescriptor tDescr = new TypeDescriptor(expr, start, end - start, 0);
if (tDescr.isArray()) {
try {
Class cls = getClassReference((Class) ctx, tDescr, variableFactory, pCtx);
// rootNode = new StaticReferenceAccessor(cls);
ldcClassConstant(cls);
return cls;
}
catch (Exception e) {
//fall through
}
}
throw new CompileException("illegal use of []: unknown type: "
+ (ctx == null ? null : ctx.getClass().getName()), expr, st);
}
}
@SuppressWarnings({"unchecked"})
private Object getMethod(Object ctx, String name)
throws IllegalAccessException, InvocationTargetException {
assert debug("\n ** {method: " + name + "}");
int st = cursor;
String tk = cursor != end && expr[cursor] == '(' && ((cursor = balancedCapture(expr, cursor, '(')) - st) > 1 ?
new String(expr, st + 1, cursor - st - 1) : "";
cursor++;
Object[] preConvArgs;
Object[] args;
Class[] argTypes;
ExecutableStatement[] es;
List subtokens;
if (tk.length() == 0) {
args = preConvArgs = ParseTools.EMPTY_OBJ_ARR;
argTypes = ParseTools.EMPTY_CLS_ARR;
es = null;
subtokens = null;
}
else {
subtokens = parseParameterList(tk.toCharArray(), 0, -1);
es = new ExecutableStatement[subtokens.size()];
args = new Object[subtokens.size()];
argTypes = new Class[subtokens.size()];
preConvArgs = new Object[es.length];
for (int i = 0; i < subtokens.size(); i++) {
assert debug("subtoken[" + i + "] { " + new String(subtokens.get(i)) + " }");
preConvArgs[i] = args[i] = (es[i] = (ExecutableStatement) subCompileExpression(subtokens.get(i), pCtx))
.getValue(this.thisRef, this.thisRef, variableFactory);
if (es[i].isExplicitCast()) argTypes[i] = es[i].getKnownEgressType();
}
if (pCtx.isStrictTypeEnforcement()) {
for (int i = 0; i < args.length; i++) {
argTypes[i] = es[i].getKnownEgressType();
if (es[i] instanceof ExecutableLiteral && ((ExecutableLiteral)es[i]).getLiteral() == null) {
argTypes[i] = NullType.class;
}
}
}
else {
for (int i = 0; i < args.length; i++) {
if (argTypes[i] != null) continue;
if (es[i].getKnownEgressType() == Object.class) {
argTypes[i] = args[i] == null ? null : args[i].getClass();
}
else {
argTypes[i] = es[i].getKnownEgressType();
}
}
}
}
if (first && variableFactory != null && variableFactory.isResolveable(name)) {
Object ptr = variableFactory.getVariableResolver(name).getValue();
if (ptr instanceof Method) {
ctx = ((Method) ptr).getDeclaringClass();
name = ((Method) ptr).getName();
}
else if (ptr instanceof MethodStub) {
ctx = ((MethodStub) ptr).getClassReference();
name = ((MethodStub) ptr).getMethodName();
}
else if (ptr instanceof FunctionInstance) {
if (es != null && es.length != 0) {
compiledInputs.addAll(Arrays.asList(es));
intPush(es.length);
assert debug("ANEWARRAY [" + es.length + "]");
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
assert debug("ASTORE 4");
mv.visitVarInsn(ASTORE, 4);
for (int i = 0; i < es.length; i++) {
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
intPush(i);
loadField(i);
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
assert debug("ALOAD 3");
mv.visitIntInsn(ALOAD, 3);
assert debug("INVOKEINTERFACE ExecutableStatement.getValue");
mv.visitMethodInsn(INVOKEINTERFACE, NAMESPACE + "compiler/ExecutableStatement", "getValue",
"(Ljava/lang/Object;L" + NAMESPACE + "integration/VariableResolverFactory;)Ljava/lang/Object;");
assert debug("AASTORE");
mv.visitInsn(AASTORE);
}
}
else {
assert debug("ACONST_NULL");
mv.visitInsn(ACONST_NULL);
assert debug("CHECKCAST java/lang/Object");
mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;");
assert debug("ASTORE 4");
mv.visitVarInsn(ASTORE, 4);
}
if (variableFactory.isIndexedFactory() && variableFactory.isTarget(name)) {
loadVariableByIndex(variableFactory.variableIndexOf(name));
}
else {
loadVariableByName(name);
}
checkcast(FunctionInstance.class);
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
assert debug("ALOAD 2");
mv.visitVarInsn(ALOAD, 2);
assert debug("ALOAD 3");
mv.visitVarInsn(ALOAD, 3);
assert debug("ALOAD 4");
mv.visitVarInsn(ALOAD, 4);
assert debug("INVOKEVIRTUAL Function.call");
mv.visitMethodInsn(INVOKEVIRTUAL,
getInternalName(FunctionInstance.class),
"call",
"(Ljava/lang/Object;Ljava/lang/Object;L" + NAMESPACE
+ "integration/VariableResolverFactory;[Ljava/lang/Object;)Ljava/lang/Object;");
return ((FunctionInstance) ptr).call(ctx, null, thisRef, variableFactory, args);
}
else {
throw new OptimizationFailure("attempt to optimize a method call for a reference that does not point to a method: "
+ name + " (reference is type: " + (ctx != null ? ctx.getClass().getName() : null) + ")");
}
first = false;
}
else if (returnType != null && returnType.isPrimitive()) {
//noinspection unchecked
wrapPrimitive(returnType);
}
if (ctx == null && currType == null) {
throw new PropertyAccessException("null pointer or function not found: " + name, this.expr, this.start, pCtx);
}
/**
* If the target object is an instance of java.lang.Class itself then do not
* adjust the Class scope target.
*/
boolean classTarget = false;
Class> cls = currType != null ? currType : ((classTarget = ctx instanceof Class) ? (Class>) ctx : ctx.getClass());
currType = null;
Method m;
Class[] parameterTypes = null;
/**
* Try to find an instance method from the class target.
*/
if ((m = getBestCandidate(argTypes, name, cls, cls.getMethods(), false, classTarget)) != null) {
parameterTypes = m.getParameterTypes();
}
if (m == null && classTarget) {
/**
* If we didn't find anything, maybe we're looking for the actual java.lang.Class methods.
*/
if ((m = getBestCandidate(argTypes, name, cls, Class.class.getMethods(), false)) != null) {
parameterTypes = m.getParameterTypes();
}
}
// If we didn't find anything and the declared class is different from the actual one try also with the actual one
if (m == null && cls != ctx.getClass() && !(ctx instanceof Class)) {
cls = ctx.getClass();
if ((m = getBestCandidate(argTypes, name, cls, cls.getMethods(), false, classTarget)) != null) {
parameterTypes = m.getParameterTypes();
}
}
if (es != null && m != null && m.isVarArgs() && (es.length != parameterTypes.length || !(es[es.length-1] instanceof ExecutableAccessor))) {
// normalize ExecutableStatement for varargs
ExecutableStatement[] varArgEs = new ExecutableStatement[parameterTypes.length];
int varArgStart = parameterTypes.length-1;
for (int i = 0; i < varArgStart; i++) varArgEs[i] = es[i];
String varargsTypeName = parameterTypes[parameterTypes.length-1].getComponentType().getName();
String varArgExpr;
if("null".equals(tk)){ //if null is the token no need for wrapping
varArgExpr = tk;
} else{
StringBuilder sb = new StringBuilder("new ").append(varargsTypeName).append("[] {");
for (int i = varArgStart; i < subtokens.size(); i++) {
sb.append(subtokens.get(i));
if (i < subtokens.size()-1) sb.append(",");
}
if (varArgStart == subtokens.size()) {
sb.append("null");
}
varArgExpr = sb.append("}").toString();
}
char[] token = varArgExpr.toCharArray();
varArgEs[varArgStart] = ((ExecutableStatement)subCompileExpression(token, pCtx));
es = varArgEs;
if (preConvArgs.length == parameterTypes.length-1) {
// empty vararg
Object[] preConvArgsForVarArg = new Object[parameterTypes.length];
for (int i = 0; i < preConvArgs.length; i++) preConvArgsForVarArg[i] = preConvArgs[i];
preConvArgsForVarArg[parameterTypes.length-1] = Array.newInstance(parameterTypes[parameterTypes.length-1].getComponentType(), 0);
preConvArgs = preConvArgsForVarArg;
}
}
int inputsOffset = compiledInputs.size();
if (es != null) {
for (ExecutableStatement e : es) {
if (e instanceof ExecutableLiteral) {
continue;
}
compiledInputs.add(e);
}
}
if (first) {
assert debug("ALOAD 1 (D) ");
mv.visitVarInsn(ALOAD, 1);
}
if (m == null) {
StringAppender errorBuild = new StringAppender();
if (parameterTypes != null) {
for (int i = 0; i < args.length; i++) {
errorBuild.append(parameterTypes[i] != null ? parameterTypes[i].getClass().getName() : null);
if (i < args.length - 1) errorBuild.append(", ");
}
}
if ("size".equals(name) && args.length == 0 && cls.isArray()) {
anyArrayCheck(cls);
assert debug("ARRAYLENGTH");
mv.visitInsn(ARRAYLENGTH);
wrapPrimitive(int.class);
return getLength(ctx);
}
throw new CompileException("unable to resolve method: " + cls.getName() + "."
+ name + "(" + errorBuild.toString() + ") [arglength=" + args.length + "]", expr, st);
}
else {
m = getWidenedTarget(m);
if (es != null) {
ExecutableStatement cExpr;
for (int i = 0; i < es.length; i++) {
if ((cExpr = es[i]).getKnownIngressType() == null) {
cExpr.setKnownIngressType(parameterTypes[i]);
cExpr.computeTypeConversionRule();
}
if (!cExpr.isConvertableIngressEgress() && i < args.length) {
args[i] = convert(args[i], paramTypeVarArgsSafe(parameterTypes, i, m.isVarArgs()));
}
}
}
else {
/**
* Coerce any types if required.
*/
for (int i = 0; i < args.length; i++) {
args[i] = convert(args[i], paramTypeVarArgsSafe(parameterTypes, i, m.isVarArgs()));
}
}
Class> declaringClass = m.getDeclaringClass();
if (m.getParameterTypes().length == 0) {
if ((m.getModifiers() & STATIC) != 0) {
assert debug("INVOKESTATIC " + m.getName());
mv.visitMethodInsn(INVOKESTATIC, getInternalName(declaringClass), m.getName(), getMethodDescriptor(m));
}
else {
assert debug("CHECKCAST " + getInternalName(declaringClass));
mv.visitTypeInsn(CHECKCAST, getInternalName(declaringClass));
if (declaringClass.isInterface()) {
assert debug("INVOKEINTERFACE " + m.getName());
mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(declaringClass), m.getName(),
getMethodDescriptor(m));
}
else {
assert debug("INVOKEVIRTUAL " + m.getName());
mv.visitMethodInsn(INVOKEVIRTUAL, getInternalName(declaringClass), m.getName(),
getMethodDescriptor(m));
}
}
returnType = m.getReturnType();
stacksize++;
}
else {
if ((m.getModifiers() & STATIC) == 0) {
assert debug("CHECKCAST " + getInternalName(declaringClass));
mv.visitTypeInsn(CHECKCAST, getInternalName(declaringClass));
}
for (int i = 0; es != null && i < es.length; i++) {
if (es[i] instanceof ExecutableLiteral) {
ExecutableLiteral literal = (ExecutableLiteral) es[i];
if (literal.getLiteral() == null) {
assert debug("ICONST_NULL");
mv.visitInsn(ACONST_NULL);
continue;
}
else if (parameterTypes[i] == int.class && literal.intOptimized()) {
intPush(literal.getInteger32());
continue;
}
else if (parameterTypes[i] == int.class && preConvArgs[i] instanceof Integer) {
intPush((Integer) preConvArgs[i]);
continue;
}
else if (parameterTypes[i] == boolean.class) {
boolean bool = DataConversion.convert(literal.getLiteral(), Boolean.class);
assert debug(bool ? "ICONST_1" : "ICONST_0");
mv.visitInsn(bool ? ICONST_1 : ICONST_0);
continue;
}
else {
Object lit = literal.getLiteral();
if (parameterTypes[i] == Object.class) {
if (isPrimitiveWrapper(lit.getClass())) {
if (lit.getClass() == Integer.class) {
intPush((Integer) lit);
}
else {
assert debug("LDC " + lit);
mv.visitLdcInsn(lit);
}
wrapPrimitive(lit.getClass());
}
else if (lit instanceof String) {
mv.visitLdcInsn(lit);
checkcast(Object.class);
}
continue;
}
else if (canConvert(parameterTypes[i], lit.getClass())) {
Object c = convert(lit, parameterTypes[i]);
if (c instanceof Class) {
ldcClassConstant((Class) c);
}
else {
assert debug("LDC " + lit + " (" + lit.getClass().getName() + ")");
mv.visitLdcInsn(c);
if (isPrimitiveWrapper(parameterTypes[i])) {
wrapPrimitive(lit.getClass());
}
}
continue;
}
else {
throw new OptimizationNotSupported();
}
}
}
assert debug("ALOAD 0");
mv.visitVarInsn(ALOAD, 0);
assert debug("GETFIELD p" + inputsOffset);
mv.visitFieldInsn(GETFIELD, className, "p" + inputsOffset, "L" + NAMESPACE + "compiler/ExecutableStatement;");
inputsOffset++;
assert debug("ALOAD 2");
mv.visitVarInsn(ALOAD, 2);
assert debug("ALOAD 3");
mv.visitVarInsn(ALOAD, 3);
assert debug("INVOKEINTERFACE ExecutableStatement.getValue");
mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(ExecutableStatement.class), "getValue",
"(Ljava/lang/Object;L" + NAMESPACE + "integration/VariableResolverFactory;)Ljava/lang/Object;");
if (parameterTypes[i].isPrimitive()) {
if (preConvArgs[i] == null ||
(parameterTypes[i] != String.class &&
!parameterTypes[i].isAssignableFrom(preConvArgs[i].getClass()))) {
ldcClassConstant(getWrapperClass(parameterTypes[i]));
assert debug("INVOKESTATIC DataConversion.convert");
mv.visitMethodInsn(INVOKESTATIC, NAMESPACE + "DataConversion", "convert",
"(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;");
}
unwrapPrimitive(parameterTypes[i]);
}
else if (preConvArgs[i] == null ||
(parameterTypes[i] != String.class &&
!parameterTypes[i].isAssignableFrom(preConvArgs[i].getClass()))) {
ldcClassConstant(parameterTypes[i]);
assert debug("INVOKESTATIC DataConversion.convert");
mv.visitMethodInsn(INVOKESTATIC, NAMESPACE + "DataConversion", "convert",
"(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;");
assert debug("CHECKCAST " + getInternalName(parameterTypes[i]));
mv.visitTypeInsn(CHECKCAST, getInternalName(parameterTypes[i]));
}
else if (parameterTypes[i] == String.class) {
assert debug("<<>");
mv.visitVarInsn(ASTORE, 4);
Label jmp = new Label();
mv.visitVarInsn(ALOAD, 4);
mv.visitJumpInsn(IFNONNULL, jmp);
mv.visitInsn(ACONST_NULL);
Label jmp2 = new Label();
mv.visitJumpInsn(GOTO, jmp2);
mv.visitLabel(jmp);
mv.visitVarInsn(ALOAD,4);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf",
"(Ljava/lang/Object;)Ljava/lang/String;");
mv.visitLabel(jmp2);
}
else {
assert debug("<<>>");
assert debug("<<>>");
assert debug("CHECKCAST " + getInternalName(parameterTypes[i]));
mv.visitTypeInsn(CHECKCAST, getInternalName(parameterTypes[i]));
}
}
if(m.isVarArgs() && (es == null || es.length == (parameterTypes.length - 1) )){
// The last parameter is a vararg and there is no value, create an empty array array
createArray(getBaseComponentType(parameterTypes[parameterTypes.length - 1]) ,0);
}
if ((m.getModifiers() & STATIC) != 0) {
assert debug("INVOKESTATIC: " + m.getName());
mv.visitMethodInsn(INVOKESTATIC, getInternalName(declaringClass), m.getName(), getMethodDescriptor(m));
}
else {
if (declaringClass.isInterface()) {
assert debug("INVOKEINTERFACE: " + getInternalName(declaringClass) + "." + m.getName());
mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(declaringClass), m.getName(),
getMethodDescriptor(m));
}
else {
assert debug("INVOKEVIRTUAL: " + getInternalName(declaringClass) + "." + m.getName());
mv.visitMethodInsn(INVOKEVIRTUAL, getInternalName(declaringClass), m.getName(),
getMethodDescriptor(m));
}
}
returnType = m.getReturnType();
stacksize++;
}
Object o = m.invoke(ctx, normalizeArgsForVarArgs(parameterTypes, args, m.isVarArgs()));
if (hasNullMethodHandler()) {
writeOutNullHandler(m, 1);
if (o == null) o = getNullMethodHandler().getProperty(m.getName(), ctx, variableFactory);
}
currType = toNonPrimitiveType(m.getReturnType());
return o;
}
}
private void dataConversion(Class target) {
if (target.equals(Object.class)) return;
ldcClassConstant(target);
assert debug("INVOKESTATIC " + NAMESPACE + "DataConversion.convert");
mv.visitMethodInsn(INVOKESTATIC, NAMESPACE + "DataConversion", "convert",
"(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;");
}
private static MVELClassLoader classLoader;
public static void setMVELClassLoader(MVELClassLoader cl) {
classLoader = cl;
}
public static MVELClassLoader getMVELClassLoader() {
return classLoader;
}
public void init() {
try {
classLoader = new JITClassLoader(currentThread().getContextClassLoader());
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private ContextClassLoader getContextClassLoader() {
return pCtx == null ? null : new ContextClassLoader(pCtx.getClassLoader());
}
private static class ContextClassLoader extends ClassLoader {
ContextClassLoader(ClassLoader classLoader) {
super(classLoader);
}
Class> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
private java.lang.Class loadClass(String className, byte[] b) throws Exception {
/**
* This must be synchronized. Two classes cannot be simultaneously deployed in the JVM.
*/
ContextClassLoader contextClassLoader = getContextClassLoader();
return contextClassLoader == null ?
classLoader.defineClassX(className, b, 0, b.length) :
contextClassLoader.defineClass(className, b);
}
private boolean debug(String instruction) {
if (buildLog != null) {
buildLog.append(instruction).append("\n");
}
return true;
}
@SuppressWarnings({"SameReturnValue"})
public String getName() {
return "ASM";
}
public Object getResultOptPass() {
return val;
}
private Class getWrapperClass(Class cls) {
if (cls == boolean.class) {
return Boolean.class;
}
else if (cls == int.class) {
return Integer.class;
}
else if (cls == float.class) {
return Float.class;
}
else if (cls == double.class) {
return Double.class;
}
else if (cls == short.class) {
return Short.class;
}
else if (cls == long.class) {
return Long.class;
}
else if (cls == byte.class) {
return Byte.class;
}
else if (cls == char.class) {
return Character.class;
}
return cls;
}
private void unwrapPrimitive(Class cls) {
if (cls == boolean.class) {
assert debug("CHECKCAST java/lang/Boolean");
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
assert debug("INVOKEVIRTUAL java/lang/Boolean.booleanValue");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
}
else if (cls == int.class) {
assert debug("CHECKCAST java/lang/Integer");
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
assert debug("INVOKEVIRTUAL java/lang/Integer.intValue");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
}
else if (cls == float.class) {
assert debug("CHECKCAST java/lang/Float");
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
assert debug("INVOKEVIRTUAL java/lang/Float.floatValue");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
}
else if (cls == double.class) {
assert debug("CHECKCAST java/lang/Double");
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
assert debug("INVOKEVIRTUAL java/lang/Double.doubleValue");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
}
else if (cls == short.class) {
assert debug("CHECKCAST java/lang/Short");
mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
assert debug("INVOKEVIRTUAL java/lang/Short.shortValue");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
}
else if (cls == long.class) {
assert debug("CHECKCAST java/lang/Long");
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
assert debug("INVOKEVIRTUAL java/lang/Long.longValue");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
}
else if (cls == byte.class) {
assert debug("CHECKCAST java/lang/Byte");
mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
assert debug("INVOKEVIRTUAL java/lang/Byte.byteValue");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
}
else if (cls == char.class) {
assert debug("CHECKCAST java/lang/Character");
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
assert debug("INVOKEVIRTUAL java/lang/Character.charValue");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
}
}
private void wrapPrimitive(Class extends Object> cls) {
if (OPCODES_VERSION == Opcodes.V1_4) {
/**
* JAVA 1.4 SUCKS! DIE 1.4 DIE!!!
*/
debug("** Using 1.4 Bytecode **");
if (cls == boolean.class || cls == Boolean.class) {
debug("NEW java/lang/Boolean");
mv.visitTypeInsn(NEW, "java/lang/Boolean");
debug("DUP X1");
mv.visitInsn(DUP_X1);
debug("SWAP");
mv.visitInsn(SWAP);
debug("INVOKESPECIAL java/lang/Boolean.::(Z)V");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Boolean", "", "(Z)V");
}
else if (cls == int.class || cls == Integer.class) {
debug("NEW java/lang/Integer");
mv.visitTypeInsn(NEW, "java/lang/Integer");
debug("DUP X1");
mv.visitInsn(DUP_X1);
debug("SWAP");
mv.visitInsn(SWAP);
debug("INVOKESPECIAL java/lang/Integer.::(I)V");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Integer", "", "(I)V");
}
else if (cls == float.class || cls == Float.class) {
debug("NEW java/lang/Float");
mv.visitTypeInsn(NEW, "java/lang/Float");
debug("DUP X1");
mv.visitInsn(DUP_X1);
debug("SWAP");
mv.visitInsn(SWAP);
debug("INVOKESPECIAL java/lang/Float.::(F)V");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Float", "", "(F)V");
}
else if (cls == double.class || cls == Double.class) {
debug("NEW java/lang/Double");
mv.visitTypeInsn(NEW, "java/lang/Double");
debug("DUP X2");
mv.visitInsn(DUP_X2);
debug("DUP X2");
mv.visitInsn(DUP_X2);
debug("POP");
mv.visitInsn(POP);
debug("INVOKESPECIAL java/lang/Double.::(D)V");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Double", "", "(D)V");
}
else if (cls == short.class || cls == Short.class) {
debug("NEW java/lang/Short");
mv.visitTypeInsn(NEW, "java/lang/Short");
debug("DUP X1");
mv.visitInsn(DUP_X1);
debug("SWAP");
mv.visitInsn(SWAP);
debug("INVOKESPECIAL java/lang/Short.::(S)V");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Short", "", "(S)V");
}
else if (cls == long.class || cls == Long.class) {
debug("NEW java/lang/Long");
mv.visitTypeInsn(NEW, "java/lang/Long");
debug("DUP X1");
mv.visitInsn(DUP_X1);
debug("SWAP");
mv.visitInsn(SWAP);
debug("INVOKESPECIAL java/lang/Long.::(L)V");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Float", "", "(L)V");
}
else if (cls == byte.class || cls == Byte.class) {
debug("NEW java/lang/Byte");
mv.visitTypeInsn(NEW, "java/lang/Byte");
debug("DUP X1");
mv.visitInsn(DUP_X1);
debug("SWAP");
mv.visitInsn(SWAP);
debug("INVOKESPECIAL java/lang/Byte.::(B)V");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Byte", "", "(B)V");
}
else if (cls == char.class || cls == Character.class) {
debug("NEW java/lang/Character");
mv.visitTypeInsn(NEW, "java/lang/Character");
debug("DUP X1");
mv.visitInsn(DUP_X1);
debug("SWAP");
mv.visitInsn(SWAP);
debug("INVOKESPECIAL java/lang/Character.::(C)V");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Character", "", "(C)V");
}
}
else {
if (cls == boolean.class || cls == Boolean.class) {
debug("INVOKESTATIC java/lang/Boolean.valueOf");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
}
else if (cls == int.class || cls == Integer.class) {
debug("INVOKESTATIC java/lang/Integer.valueOf");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
}
else if (cls == float.class || cls == Float.class) {
debug("INVOKESTATIC java/lang/Float.valueOf");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
}
else if (cls == double.class || cls == Double.class) {
debug("INVOKESTATIC java/lang/Double.valueOf");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
}
else if (cls == short.class || cls == Short.class) {
debug("INVOKESTATIC java/lang/Short.valueOf");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
}
else if (cls == long.class || cls == Long.class) {
debug("INVOKESTATIC java/lang/Long.valueOf");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
}
else if (cls == byte.class || cls == Byte.class) {
debug("INVOKESTATIC java/lang/Byte.valueOf");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
}
else if (cls == char.class || cls == Character.class) {
debug("INVOKESTATIC java/lang/Character.valueOf");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
}
}
}
private void anyArrayCheck(Class cls) {
if (cls == boolean[].class) {
assert debug("CHECKCAST [Z");
mv.visitTypeInsn(CHECKCAST, "[Z");
}
else if (cls == int[].class) {
assert debug("CHECKCAST [I");
mv.visitTypeInsn(CHECKCAST, "[I");
}
else if (cls == float[].class) {
assert debug("CHECKCAST [F");
mv.visitTypeInsn(CHECKCAST, "[F");
}
else if (cls == double[].class) {
assert debug("CHECKCAST [D");
mv.visitTypeInsn(CHECKCAST, "[D");
}
else if (cls == short[].class) {
assert debug("CHECKCAST [S");
mv.visitTypeInsn(CHECKCAST, "[S");
}
else if (cls == long[].class) {
assert debug("CHECKCAST [J");
mv.visitTypeInsn(CHECKCAST, "[J");
}
else if (cls == byte[].class) {
assert debug("CHECKCAST [B");
mv.visitTypeInsn(CHECKCAST, "[B");
}
else if (cls == char[].class) {
assert debug("CHECKCAST [C");
mv.visitTypeInsn(CHECKCAST, "[C");
}
else {
assert debug("CHECKCAST [Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;");
}
}
private void writeOutLiteralWrapped(Object lit) {
if (lit instanceof Integer) {
intPush((Integer) lit);
wrapPrimitive(int.class);
return;
}
assert debug("LDC " + lit);
if (lit instanceof String) {
mv.visitLdcInsn(lit);
}
else if (lit instanceof Long) {
mv.visitLdcInsn(lit);
wrapPrimitive(long.class);
}
else if (lit instanceof Float) {
mv.visitLdcInsn(lit);
wrapPrimitive(float.class);
}
else if (lit instanceof Double) {
mv.visitLdcInsn(lit);
wrapPrimitive(double.class);
}
else if (lit instanceof Short) {
mv.visitLdcInsn(lit);
wrapPrimitive(short.class);
}
else if (lit instanceof Character) {
mv.visitLdcInsn(lit);
wrapPrimitive(char.class);
}
else if (lit instanceof Boolean) {
mv.visitLdcInsn(lit);
wrapPrimitive(boolean.class);
}
else if (lit instanceof Byte) {
mv.visitLdcInsn(lit);
wrapPrimitive(byte.class);
}
}
/**
* Gets the ASM instruction operand for the given primitive type.
* Will throw IllegalStateException if the type is not primitive.
* @param c The class representing the primitive type.
* @return The operand
*/
public static int toPrimitiveTypeOperand(Class> c) {
if (c == int.class) return Opcodes.T_INT;
if (c == long.class) return Opcodes.T_LONG;
if (c == double.class) return Opcodes.T_DOUBLE;
if (c == float.class) return Opcodes.T_FLOAT;
if (c == short.class) return Opcodes.T_SHORT;
if (c == byte.class) return Opcodes.T_BYTE;
if (c == char.class) return Opcodes.T_CHAR;
if (c == boolean.class) return Opcodes.T_BOOLEAN;
throw new IllegalStateException("Non-primitive type passed to toPrimitiveTypeOperand: " + c);
}
/**
* Create an array of any type (primitive or reference)
* @param componentType The type of array elements
* @param length The length of the array
*/
private void createArray(Class componentType, int length) {
intPush(length);
if (componentType.isPrimitive()) {
assert debug("NEWARRAY " + getInternalName(componentType) + " (" + length + ")");
mv.visitIntInsn(NEWARRAY, toPrimitiveTypeOperand(componentType));
}
else {
assert debug("ANEWARRAY " + getInternalName(componentType) + " (" + length + ")");
mv.visitTypeInsn(ANEWARRAY, getInternalName(componentType));
}
}
public void arrayStore(Class cls) {
if (cls.isPrimitive()) {
if (cls == int.class) {
assert debug("IASTORE");
mv.visitInsn(IASTORE);
}
else if (cls == char.class) {
assert debug("CASTORE");
mv.visitInsn(CASTORE);
}
else if (cls == boolean.class) {
assert debug("BASTORE");
mv.visitInsn(BASTORE);
}
else if (cls == double.class) {
assert debug("DASTORE");
mv.visitInsn(DASTORE);
}
else if (cls == float.class) {
assert debug("FASTORE");
mv.visitInsn(FASTORE);
}
else if (cls == short.class) {
assert debug("SASTORE");
mv.visitInsn(SASTORE);
}
else if (cls == long.class) {
assert debug("LASTORE");
mv.visitInsn(LASTORE);
}
else if (cls == byte.class) {
assert debug("BASTORE");
mv.visitInsn(BASTORE);
}
}
else {
assert debug("AASTORE");
mv.visitInsn(AASTORE);
}
}
public void wrapRuntimeConverstion(Class toType) {
ldcClassConstant(getWrapperClass(toType));
assert debug("INVOKESTATIC DataConversion.convert");
mv.visitMethodInsn(INVOKESTATIC, "" + NAMESPACE + "DataConversion", "convert",
"(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;");
}
private Object addSubstatement(ExecutableStatement stmt) {
compiledInputs.add(stmt);
assert debug("ALOAD 0");
mv.visitVarInsn(ALOAD, 0);
assert debug("GETFIELD p" + (compiledInputs.size() - 1));
mv.visitFieldInsn(GETFIELD, className, "p" + (compiledInputs.size() - 1),
"L" + NAMESPACE + "compiler/ExecutableStatement;");
assert debug("ALOAD 2");
mv.visitVarInsn(ALOAD, 2);
assert debug("ALOAD 3");
mv.visitVarInsn(ALOAD, 3);
assert debug("INVOKEINTERFACE ExecutableStatement.getValue");
mv.visitMethodInsn(INVOKEINTERFACE, getInternalName(ExecutableStatement.class), "getValue",
"(Ljava/lang/Object;L" + NAMESPACE + "integration/VariableResolverFactory;)Ljava/lang/Object;");
return null;
}
private void loadVariableByName(String name) {
assert debug("ALOAD 3");
mv.visitVarInsn(ALOAD, 3);
assert debug("LDC \"" + name + "\"");
mv.visitLdcInsn(name);
assert debug("INVOKEINTERFACE " + NAMESPACE + "integration/VariableResolverFactory.getVariableResolver");
mv.visitMethodInsn(INVOKEINTERFACE, "" + NAMESPACE + "integration/VariableResolverFactory",
"getVariableResolver", "(Ljava/lang/String;)L" + NAMESPACE + "integration/VariableResolver;");
assert debug("INVOKEINTERFACE " + NAMESPACE + "integration/VariableResolver.getValue");
mv.visitMethodInsn(INVOKEINTERFACE, "" + NAMESPACE + "integration/VariableResolver",
"getValue", "()Ljava/lang/Object;");
returnType = Object.class;
}
private void loadVariableByIndex(int pos) {
assert debug("ALOAD 3");
mv.visitVarInsn(ALOAD, 3);
assert debug("PUSH IDX VAL =" + pos);
intPush(pos);
assert debug("INVOKEINTERFACE " + NAMESPACE + "integration/VariableResolverFactory.getIndexedVariableResolver");
mv.visitMethodInsn(INVOKEINTERFACE, "" + NAMESPACE + "integration/VariableResolverFactory",
"getIndexedVariableResolver", "(I)L" + NAMESPACE + "integration/VariableResolver;");
assert debug("INVOKEINTERFACE " + NAMESPACE + "integration/VariableResolver.getValue");
mv.visitMethodInsn(INVOKEINTERFACE, "" + NAMESPACE + "integration/VariableResolver",
"getValue", "()Ljava/lang/Object;");
returnType = Object.class;
}
private void loadField(int number) {
assert debug("ALOAD 0");
mv.visitVarInsn(ALOAD, 0);
assert debug("GETFIELD p" + number);
mv.visitFieldInsn(GETFIELD, className, "p" + number, "L" + NAMESPACE + "compiler/ExecutableStatement;");
}
private void ldcClassConstant(Class cls) {
if (OPCODES_VERSION == Opcodes.V1_4) {
assert debug("LDC \"" + cls.getName() + "\"");
mv.visitLdcInsn(cls.getName());
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
Label l4 = new Label();
mv.visitJumpInsn(GOTO, l4);
mv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError");
mv.visitInsn(DUP_X1);
mv.visitInsn(SWAP);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Throwable", "getMessage", "()Ljava/lang/String;");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoClassDefFoundError", "", "(Ljava/lang/String;)V");
mv.visitInsn(ATHROW);
mv.visitLabel(l4);
}
else {
assert debug("LDC " + getType(cls));
mv.visitLdcInsn(getType(cls));
}
}
private void buildInputs() {
if (compiledInputs.size() == 0) return;
assert debug("\n{SETTING UP MEMBERS...}\n");
StringAppender constSig = new StringAppender("(");
int size = compiledInputs.size();
for (int i = 0; i < size; i++) {
assert debug("ACC_PRIVATE p" + i);
cw.visitField(ACC_PRIVATE, "p" + i, "L" + NAMESPACE + "compiler/ExecutableStatement;", null, null).visitEnd();
constSig.append("L" + NAMESPACE + "compiler/ExecutableStatement;");
}
constSig.append(")V");
assert debug("\n{CREATING INJECTION CONSTRUCTOR}\n");
MethodVisitor cv = cw.visitMethod(ACC_PUBLIC, "", constSig.toString(), null, null);
cv.visitCode();
assert debug("ALOAD 0");
cv.visitVarInsn(ALOAD, 0);
assert debug("INVOKESPECIAL java/lang/Object.");
cv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V");
for (int i = 0; i < size; i++) {
assert debug("ALOAD 0");
cv.visitVarInsn(ALOAD, 0);
assert debug("ALOAD " + (i + 1));
cv.visitVarInsn(ALOAD, i + 1);
assert debug("PUTFIELD p" + i);
cv.visitFieldInsn(PUTFIELD, className, "p" + i, "L" + NAMESPACE + "compiler/ExecutableStatement;");
}
assert debug("RETURN");
cv.visitInsn(RETURN);
cv.visitMaxs(0, 0);
cv.visitEnd();
assert debug("}");
}
private static final int ARRAY = 0;
private static final int LIST = 1;
private static final int MAP = 2;
private static final int VAL = 3;
private int _getAccessor(Object o, Class type) {
if (o instanceof List) {
assert debug("NEW " + LIST_IMPL);
mv.visitTypeInsn(NEW, LIST_IMPL);
assert debug("DUP");
mv.visitInsn(DUP);
assert debug("DUP");
mv.visitInsn(DUP);
intPush(((List) o).size());
assert debug("INVOKESPECIAL " + LIST_IMPL + ".");
mv.visitMethodInsn(INVOKESPECIAL, LIST_IMPL, "", "(I)V");
for (Object item : (List) o) {
if (_getAccessor(item, type) != VAL) {
assert debug("POP");
mv.visitInsn(POP);
}
assert debug("INVOKEINTERFACE java/util/List.add");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z");
assert debug("POP");
mv.visitInsn(POP);
assert debug("DUP");
mv.visitInsn(DUP);
}
returnType = List.class;
return LIST;
}
else if (o instanceof Map) {
assert debug("NEW " + MAP_IMPL);
mv.visitTypeInsn(NEW, MAP_IMPL);
assert debug("DUP");
mv.visitInsn(DUP);
assert debug("DUP");
mv.visitInsn(DUP);
intPush(((Map) o).size());
assert debug("INVOKESPECIAL " + MAP_IMPL + ".");
mv.visitMethodInsn(INVOKESPECIAL, MAP_IMPL, "", "(I)V");
for (Object item : ((Map) o).keySet()) {
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
Object key = item;
int res = -1;
if (key instanceof String && ((String) key).trim().length() > 0) {
Serializable cKey = subCompileExpression(((String) key).toCharArray(), pCtx);
if (cKey instanceof ExecutableAccessor) {
String literal = ((ExecutableAccessor)cKey).getNode().getName();
ExecutableLiteral executableLiteral = new ExecutableLiteral(literal);
writeLiteralOrSubexpression(executableLiteral);
res = VAL;
}
}
if (res == -1) {
res = _getAccessor(key, type);
}
if (res != VAL) {
assert debug("POP");
mv.visitInsn(POP);
}
if (_getAccessor(((Map) o).get(item), type) != VAL) {
assert debug("POP");
mv.visitInsn(POP);
}
assert debug("INVOKEINTERFACE java/util/Map.put");
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
assert debug("POP");
mv.visitInsn(POP);
assert debug("DUP");
mv.visitInsn(DUP);
}
returnType = Map.class;
return MAP;
}
else if (o instanceof Object[]) {
Accessor[] a = new Accessor[((Object[]) o).length];
int i = 0;
int dim = 0;
if (type != null) {
String nm = type.getName();
while (nm.charAt(dim) == '[') dim++;
}
else {
type = Object[].class;
dim = 1;
}
try {
Class componentType = getSubComponentType(type);
createArray(componentType, ((Object[])o).length);
Class cls = dim > 1 ? findClass(null, repeatChar('[', dim - 1)
+ "L" + getBaseComponentType(type).getName() + ";", pCtx)
: toNonPrimitiveArray(type);
assert debug("DUP");
mv.visitInsn(DUP);
for (Object item : (Object[]) o) {
intPush(i);
if (_getAccessor(item, cls) != VAL) {
assert debug("POP");
mv.visitInsn(POP);
}
if (componentType.isPrimitive()) {
unwrapPrimitive(componentType);
}
arrayStore(componentType);
assert debug("DUP");
mv.visitInsn(DUP);
i++;
}
}
catch (ClassNotFoundException e) {
throw new RuntimeException("this error should never throw:" + getBaseComponentType(type).getName(), e);
}
return ARRAY;
}
else {
if (type.isArray()) {
writeLiteralOrSubexpression(subCompileExpression(((String) o).toCharArray(), pCtx), getSubComponentType(type));
}
else {
writeLiteralOrSubexpression(subCompileExpression(((String) o).toCharArray(), pCtx));
}
return VAL;
}
}
private Class writeLiteralOrSubexpression(Object stmt) {
return writeLiteralOrSubexpression(stmt, null, null);
}
private Class writeLiteralOrSubexpression(Object stmt, Class desiredTarget) {
return writeLiteralOrSubexpression(stmt, desiredTarget, null);
}
private Class writeLiteralOrSubexpression(Object stmt, Class desiredTarget, Class knownIngressType) {
if (stmt instanceof ExecutableLiteral) {
Object literalValue = ((ExecutableLiteral) stmt).getLiteral();
// Handle the case when the literal is null MVEL-312
if(literalValue == null){
mv.visitInsn(ACONST_NULL);
return null;
}
Class type = literalValue == null ? desiredTarget : literalValue.getClass();
assert debug("*** type:" + type + ";desired:" + desiredTarget);
if (type == Integer.class && desiredTarget == int.class) {
intPush(((ExecutableLiteral) stmt).getInteger32());
type = int.class;
}
else if (desiredTarget != null && desiredTarget != type) {
assert debug("*** Converting because desiredType(" + desiredTarget.getClass() + ") is not: " + type);
if (!DataConversion.canConvert(type, desiredTarget)) {
throw new CompileException("was expecting type: " + desiredTarget.getName()
+ "; but found type: " + type.getName(), expr, st);
}
writeOutLiteralWrapped(convert(literalValue, desiredTarget));
}
else {
writeOutLiteralWrapped(literalValue);
}
return type;
}
else {
literal = false;
addSubstatement((ExecutableStatement) stmt);
Class type;
if (knownIngressType == null) {
type = ((ExecutableStatement) stmt).getKnownEgressType();
}
else {
type = knownIngressType;
}
if (desiredTarget != null && type != desiredTarget) {
if (desiredTarget.isPrimitive()) {
if (type == null) throw new OptimizationFailure("cannot optimize expression: " + new String(expr) +
": cannot determine ingress type for primitive output");
checkcast(type);
unwrapPrimitive(desiredTarget);
}
}
return type;
}
}
private void addPrintOut(String text) {
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn(text);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
}
public Accessor optimizeCollection(ParserContext pCtx, Object o, Class type, char[] property, int start, int offset,
Object ctx, Object thisRef, VariableResolverFactory factory) {
this.expr = property;
this.cursor = this.start = start;
this.end = start + offset;
this.length = offset;
this.returnType = type;
this.compiledInputs = new ArrayList();
this.ctx = ctx;
this.thisRef = thisRef;
this.variableFactory = factory;
this.pCtx = pCtx;
_initJIT();
literal = true;
_getAccessor(o, type);
_finishJIT();
try {
Accessor compiledAccessor = _initializeAccessor();
if (property != null && length > start) {
return new Union(pCtx, compiledAccessor, property, start, length);
}
else {
return compiledAccessor;
}
}
catch (Exception e) {
throw new OptimizationFailure("could not optimize collection", e);
}
}
private void checkcast(Class cls) {
assert debug("CHECKCAST " + getInternalName(cls));
mv.visitTypeInsn(CHECKCAST, getInternalName(cls));
}
private void intPush(int index) {
if (index >= 0 && index < 6) {
switch (index) {
case 0:
assert debug("ICONST_0");
mv.visitInsn(ICONST_0);
break;
case 1:
assert debug("ICONST_1");
mv.visitInsn(ICONST_1);
break;
case 2:
assert debug("ICONST_2");
mv.visitInsn(ICONST_2);
break;
case 3:
assert debug("ICONST_3");
mv.visitInsn(ICONST_3);
break;
case 4:
assert debug("ICONST_4");
mv.visitInsn(ICONST_4);
break;
case 5:
assert debug("ICONST_5");
mv.visitInsn(ICONST_5);
break;
}
}
else if (index > -127 && index < 128) {
assert debug("BIPUSH " + index);
mv.visitIntInsn(BIPUSH, index);
}
else if (index > Short.MAX_VALUE) {
assert debug("LDC " + index);
mv.visitLdcInsn(index);
}
else {
assert debug("SIPUSH " + index);
mv.visitIntInsn(SIPUSH, index);
}
}
public Accessor optimizeObjectCreation(ParserContext pCtx, char[] property, int start, int offset, Object ctx,
Object thisRef, VariableResolverFactory factory) {
_initJIT();
compiledInputs = new ArrayList();
this.start = cursor = start;
this.end = start + offset;
this.length = this.end - this.start;
this.ctx = ctx;
this.thisRef = thisRef;
this.variableFactory = factory;
this.pCtx = pCtx;
String[] cnsRes = captureContructorAndResidual(property, start, offset);
List constructorParms = parseMethodOrConstructor(cnsRes[0].toCharArray());
try {
if (constructorParms != null) {
for (char[] constructorParm : constructorParms) {
compiledInputs.add((ExecutableStatement) subCompileExpression(constructorParm, pCtx));
}
Class cls = findClass(factory, new String(subset(property, 0, findFirst('(', start, length, property))), pCtx);
assert debug("NEW " + getInternalName(cls));
mv.visitTypeInsn(NEW, getInternalName(cls));
assert debug("DUP");
mv.visitInsn(DUP);
Object[] parms = new Object[constructorParms.size()];
int i = 0;
for (ExecutableStatement es : compiledInputs) {
parms[i++] = es.getValue(ctx, factory);
}
Constructor cns = getBestConstructorCandidate(parms, cls, pCtx.isStrongTyping());
if (cns == null) {
StringBuilder error = new StringBuilder();
for (int x = 0; x < parms.length; x++) {
error.append(parms[x].getClass().getName());
if (x + 1 < parms.length) error.append(", ");
}
throw new CompileException("unable to find constructor: " + cls.getName()
+ "(" + error.toString() + ")", expr, st);
}
this.returnType = cns.getDeclaringClass();
Class[] parameterTypes = cns.getParameterTypes();
Class tg;
Class> paramType = null;
int vaStart = -1;
for (i = 0; i < constructorParms.size(); i++) {
if (i < parameterTypes.length) {
paramType = parameterTypes[i];
if (cns.isVarArgs() && i == parameterTypes.length - 1) {
paramType = getBaseComponentType(paramType);
vaStart = i;
createArray(paramType, constructorParms.size() - vaStart);
}
}
else {
if (vaStart < 0 || paramType == null) {
throw new IllegalStateException("Incorrect argument count " + i);
}
}
if (vaStart >= 0) {
assert debug("DUP");
mv.visitInsn(DUP);
intPush(i - vaStart);
}
assert debug("ALOAD 0");
mv.visitVarInsn(ALOAD, 0);
assert debug("GETFIELD p" + i);
mv.visitFieldInsn(GETFIELD, className, "p" + i, "L" + NAMESPACE + "compiler/ExecutableStatement;");
assert debug("ALOAD 2");
mv.visitVarInsn(ALOAD, 2);
assert debug("ALOAD 3");
mv.visitVarInsn(ALOAD, 3);
assert debug("INVOKEINTERFACE " + NAMESPACE + "compiler/ExecutableStatement.getValue");
mv.visitMethodInsn(INVOKEINTERFACE, "" + NAMESPACE
+ "compiler/ExecutableStatement", "getValue", "(Ljava/lang/Object;L" + NAMESPACE
+ "integration/VariableResolverFactory;)Ljava/lang/Object;");
tg = paramType.isPrimitive()
? getWrapperClass(paramType) : paramType;
if (parms[i] != null && !parms[i].getClass().isAssignableFrom(paramType)) {
ldcClassConstant(tg);
assert debug("INVOKESTATIC " + NAMESPACE + "DataConversion.convert");
mv.visitMethodInsn(INVOKESTATIC, "" + NAMESPACE + "DataConversion", "convert",
"(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;");
if (paramType.isPrimitive()) {
unwrapPrimitive(paramType);
}
else {
assert debug("CHECKCAST " + getInternalName(tg));
mv.visitTypeInsn(CHECKCAST, getInternalName(tg));
}
}
else {
assert debug("CHECKCAST " + getInternalName(paramType));
mv.visitTypeInsn(CHECKCAST, getInternalName(paramType));
}
if (vaStart >= 0) {
arrayStore(paramType);
}
}
if (i < parameterTypes.length && cns.isVarArgs()) {
// The last parameter is a vararg and there is no value, create an empty array array
createArray(getBaseComponentType(parameterTypes[i]) ,0);
}
assert debug("INVOKESPECIAL " + getInternalName(cls) + ". : " + getConstructorDescriptor(cns));
mv.visitMethodInsn(INVOKESPECIAL, getInternalName(cls), "", getConstructorDescriptor(cns));
_finishJIT();
Accessor acc = _initializeAccessor();
if (cnsRes.length > 1 && cnsRes[1] != null && !cnsRes[1].trim().equals("")) {
return new Union(pCtx, acc, cnsRes[1].toCharArray(), 0, cnsRes[1].length());
}
return acc;
}
else {
Class cls = findClass(factory, new String(property), pCtx);
assert debug("NEW " + getInternalName(cls));
mv.visitTypeInsn(NEW, getInternalName(cls));
assert debug("DUP");
mv.visitInsn(DUP);
Constructor cns = cls.getConstructor(EMPTYCLS);
assert debug("INVOKESPECIAL ");
mv.visitMethodInsn(INVOKESPECIAL, getInternalName(cls), "", getConstructorDescriptor(cns));
_finishJIT();
Accessor acc = _initializeAccessor();
if (cnsRes.length > 1 && cnsRes[1] != null && !cnsRes[1].trim().equals("")) {
return new Union(pCtx, acc, cnsRes[1].toCharArray(), 0, cnsRes[1].length());
}
return acc;
}
}
catch (ClassNotFoundException e) {
throw new CompileException("class or class reference not found: "
+ new String(property), property, st);
}
catch (Exception e) {
throw new OptimizationFailure("could not optimize construtor: " + new String(property), e);
}
}
public Class getEgressType() {
return returnType;
}
private void dumpAdvancedDebugging() {
if (buildLog == null) return;
System.out.println("JIT Compiler Dump for: <<" + (expr == null ? null : new String(expr))
+ ">>\n-------------------------------\n");
System.out.println(buildLog.toString());
System.out.println("\n\n");
if (MVEL.isFileDebugging()) {
try {
FileWriter writer = ParseTools.getDebugFileWriter();
writer.write(buildLog.toString());
writer.flush();
writer.close();
}
catch (IOException e) {
//empty
}
}
}
private Object propHandlerByteCode(String property, Object ctx, Class handler) {
PropertyHandler ph = getPropertyHandler(handler);
if (ph instanceof ProducesBytecode) {
assert debug("<<3rd-Party Code Generation>>");
((ProducesBytecode) ph).produceBytecodeGet(mv, property, variableFactory);
return ph.getProperty(property, ctx, variableFactory);
}
else {
throw new RuntimeException("unable to compileShared: custom accessor does not support producing bytecode: "
+ ph.getClass().getName());
}
}
private void propHandlerByteCodePut(String property, Object ctx, Class handler, Object value) {
PropertyHandler ph = getPropertyHandler(handler);
if (ph instanceof ProducesBytecode) {
assert debug("<<3rd-Party Code Generation>>");
((ProducesBytecode) ph).produceBytecodePut(mv, property, variableFactory);
ph.setProperty(property, ctx, variableFactory, value);
}
else {
throw new RuntimeException("unable to compileShared: custom accessor does not support producing bytecode: "
+ ph.getClass().getName());
}
}
private void writeOutNullHandler(Member member, int type) {
assert debug("DUP");
mv.visitInsn(DUP);
Label j = new Label();
assert debug("IFNONNULL : jump");
mv.visitJumpInsn(IFNONNULL, j);
assert debug("POP");
mv.visitInsn(POP);
assert debug("ALOAD 0");
mv.visitVarInsn(ALOAD, 0);
if (type == 0) {
this.propNull = true;
assert debug("GETFIELD 'nullPropertyHandler'");
mv.visitFieldInsn(GETFIELD, className, "nullPropertyHandler", "L" + NAMESPACE + "integration/PropertyHandler;");
}
else {
this.methNull = true;
assert debug("GETFIELD 'nullMethodHandler'");
mv.visitFieldInsn(GETFIELD, className, "nullMethodHandler", "L" + NAMESPACE + "integration/PropertyHandler;");
}
assert debug("LDC '" + member.getName() + "'");
mv.visitLdcInsn(member.getName());
assert debug("ALOAD 1");
mv.visitVarInsn(ALOAD, 1);
assert debug("ALOAD 3");
mv.visitVarInsn(ALOAD, 3);
assert debug("INVOKEINTERFACE PropertyHandler.getProperty");
mv.visitMethodInsn(INVOKEINTERFACE, NAMESPACE + "integration/PropertyHandler", "getProperty",
"(Ljava/lang/String;Ljava/lang/Object;L" + NAMESPACE + "integration/VariableResolverFactory;)Ljava/lang/Object;");
assert debug("LABEL:jump");
mv.visitLabel(j);
}
public boolean isLiteralOnly() {
return literal;
}
}