001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.expression.spel.ast; 018 019import java.lang.reflect.Array; 020 021import org.springframework.asm.MethodVisitor; 022import org.springframework.asm.Type; 023import org.springframework.expression.EvaluationException; 024import org.springframework.expression.TypedValue; 025import org.springframework.expression.spel.CodeFlow; 026import org.springframework.expression.spel.ExpressionState; 027import org.springframework.lang.Nullable; 028import org.springframework.util.Assert; 029 030/** 031 * Represents a reference to a type, for example 032 * {@code "T(String)" or "T(com.somewhere.Foo)"}. 033 * 034 * @author Andy Clement 035 */ 036public class TypeReference extends SpelNodeImpl { 037 038 private final int dimensions; 039 040 @Nullable 041 private transient Class<?> type; 042 043 044 public TypeReference(int startPos, int endPos, SpelNodeImpl qualifiedId) { 045 this(startPos, endPos, qualifiedId, 0); 046 } 047 048 public TypeReference(int startPos, int endPos, SpelNodeImpl qualifiedId, int dims) { 049 super(startPos, endPos, qualifiedId); 050 this.dimensions = dims; 051 } 052 053 054 @Override 055 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { 056 // TODO possible optimization here if we cache the discovered type reference, but can we do that? 057 String typeName = (String) this.children[0].getValueInternal(state).getValue(); 058 Assert.state(typeName != null, "No type name"); 059 if (!typeName.contains(".") && Character.isLowerCase(typeName.charAt(0))) { 060 TypeCode tc = TypeCode.valueOf(typeName.toUpperCase()); 061 if (tc != TypeCode.OBJECT) { 062 // It is a primitive type 063 Class<?> clazz = makeArrayIfNecessary(tc.getType()); 064 this.exitTypeDescriptor = "Ljava/lang/Class"; 065 this.type = clazz; 066 return new TypedValue(clazz); 067 } 068 } 069 Class<?> clazz = state.findType(typeName); 070 clazz = makeArrayIfNecessary(clazz); 071 this.exitTypeDescriptor = "Ljava/lang/Class"; 072 this.type = clazz; 073 return new TypedValue(clazz); 074 } 075 076 private Class<?> makeArrayIfNecessary(Class<?> clazz) { 077 if (this.dimensions != 0) { 078 for (int i = 0; i < this.dimensions; i++) { 079 Object array = Array.newInstance(clazz, 0); 080 clazz = array.getClass(); 081 } 082 } 083 return clazz; 084 } 085 086 @Override 087 public String toStringAST() { 088 StringBuilder sb = new StringBuilder("T("); 089 sb.append(getChild(0).toStringAST()); 090 for (int d = 0; d < this.dimensions; d++) { 091 sb.append("[]"); 092 } 093 sb.append(")"); 094 return sb.toString(); 095 } 096 097 @Override 098 public boolean isCompilable() { 099 return (this.exitTypeDescriptor != null); 100 } 101 102 @Override 103 public void generateCode(MethodVisitor mv, CodeFlow cf) { 104 // TODO Future optimization - if followed by a static method call, skip generating code here 105 Assert.state(this.type != null, "No type available"); 106 if (this.type.isPrimitive()) { 107 if (this.type == Boolean.TYPE) { 108 mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); 109 } 110 else if (this.type == Byte.TYPE) { 111 mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); 112 } 113 else if (this.type == Character.TYPE) { 114 mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); 115 } 116 else if (this.type == Double.TYPE) { 117 mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); 118 } 119 else if (this.type == Float.TYPE) { 120 mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); 121 } 122 else if (this.type == Integer.TYPE) { 123 mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); 124 } 125 else if (this.type == Long.TYPE) { 126 mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); 127 } 128 else if (this.type == Short.TYPE) { 129 mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); 130 } 131 } 132 else { 133 mv.visitLdcInsn(Type.getType(this.type)); 134 } 135 cf.pushDescriptor(this.exitTypeDescriptor); 136 } 137 138}