/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.compiler;
import javassist.*;
import javassist.compiler.ast.*;
/* Type checker accepting extended Java syntax for Javassist.
*/
public class JvstTypeChecker extends TypeChecker {
private JvstCodeGen codeGen;
public JvstTypeChecker(CtClass cc, ClassPool cp, JvstCodeGen gen) {
super(cc, cp);
codeGen = gen;
}
/* If the type of the expression compiled last is void,
* add ACONST_NULL and change exprType, arrayDim, className.
*/
public void addNullIfVoid() {
if (exprType == VOID) {
exprType = CLASS;
arrayDim = 0;
className = jvmJavaLangObject;
}
}
/* To support $args, $sig, and $type.
* $args is an array of parameter list.
*/
public void atMember(Member mem) throws CompileError {
String name = mem.get();
if (name.equals(codeGen.paramArrayName)) {
exprType = CLASS;
arrayDim = 1;
className = jvmJavaLangObject;
}
else if (name.equals(JvstCodeGen.sigName)) {
exprType = CLASS;
arrayDim = 1;
className = "java/lang/Class";
}
else if (name.equals(JvstCodeGen.dollarTypeName)
|| name.equals(JvstCodeGen.clazzName)) {
exprType = CLASS;
arrayDim = 0;
className = "java/lang/Class";
}
else
super.atMember(mem);
}
protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right)
throws CompileError
{
if (left instanceof Member
&& ((Member)left).get().equals(codeGen.paramArrayName)) {
right.accept(this);
CtClass[] params = codeGen.paramTypeList;
if (params == null)
return;
int n = params.length;
for (int i = 0; i < n; ++i)
compileUnwrapValue(params[i]);
}
else
super.atFieldAssign(expr, op, left, right);
}
public void atCastExpr(CastExpr expr) throws CompileError {
ASTList classname = expr.getClassName();
if (classname != null && expr.getArrayDim() == 0) {
ASTree p = classname.head();
if (p instanceof Symbol && classname.tail() == null) {
String typename = ((Symbol)p).get();
if (typename.equals(codeGen.returnCastName)) {
atCastToRtype(expr);
return;
}
else if (typename.equals(JvstCodeGen.wrapperCastName)) {
atCastToWrapper(expr);
return;
}
}
}
super.atCastExpr(expr);
}
/**
* Inserts a cast operator to the return type.
* If the return type is void, this does nothing.
*/
protected void atCastToRtype(CastExpr expr) throws CompileError {
CtClass returnType = codeGen.returnType;
expr.getOprand().accept(this);
if (exprType == VOID || CodeGen.isRefType(exprType) || arrayDim > 0)
compileUnwrapValue(returnType);
else if (returnType instanceof CtPrimitiveType) {
CtPrimitiveType pt = (CtPrimitiveType)returnType;
int destType = MemberResolver.descToType(pt.getDescriptor());
exprType = destType;
arrayDim = 0;
className = null;
}
}
protected void atCastToWrapper(CastExpr expr) throws CompileError {
expr.getOprand().accept(this);
if (CodeGen.isRefType(exprType) || arrayDim > 0)
return; // Object type. do nothing.
CtClass clazz = resolver.lookupClass(exprType, arrayDim, className);
if (clazz instanceof CtPrimitiveType) {
exprType = CLASS;
arrayDim = 0;
className = jvmJavaLangObject;
}
}
/* Delegates to a ProcHandler object if the method call is
* $proceed(). It may process $cflow().
*/
public void atCallExpr(CallExpr expr) throws CompileError {
ASTree method = expr.oprand1();
if (method instanceof Member) {
String name = ((Member)method).get();
if (codeGen.procHandler != null
&& name.equals(codeGen.proceedName)) {
codeGen.procHandler.setReturnType(this,
(ASTList)expr.oprand2());
return;
}
else if (name.equals(JvstCodeGen.cflowName)) {
atCflow((ASTList)expr.oprand2());
return;
}
}
super.atCallExpr(expr);
}
/* To support $cflow().
*/
protected void atCflow(ASTList cname) throws CompileError {
exprType = INT;
arrayDim = 0;
className = null;
}
/* To support $$. ($$) is equivalent to ($1, ..., $n).
* It can be used only as a parameter list of method call.
*/
public boolean isParamListName(ASTList args) {
if (codeGen.paramTypeList != null
&& args != null && args.tail() == null) {
ASTree left = args.head();
return (left instanceof Member
&& ((Member)left).get().equals(codeGen.paramListName));
}
else
return false;
}
public int getMethodArgsLength(ASTList args) {
String pname = codeGen.paramListName;
int n = 0;
while (args != null) {
ASTree a = args.head();
if (a instanceof Member && ((Member)a).get().equals(pname)) {
if (codeGen.paramTypeList != null)
n += codeGen.paramTypeList.length;
}
else
++n;
args = args.tail();
}
return n;
}
public void atMethodArgs(ASTList args, int[] types, int[] dims,
String[] cnames) throws CompileError {
CtClass[] params = codeGen.paramTypeList;
String pname = codeGen.paramListName;
int i = 0;
while (args != null) {
ASTree a = args.head();
if (a instanceof Member && ((Member)a).get().equals(pname)) {
if (params != null) {
int n = params.length;
for (int k = 0; k < n; ++k) {
CtClass p = params[k];
setType(p);
types[i] = exprType;
dims[i] = arrayDim;
cnames[i] = className;
++i;
}
}
}
else {
a.accept(this);
types[i] = exprType;
dims[i] = arrayDim;
cnames[i] = className;
++i;
}
args = args.tail();
}
}
/* called by Javac#recordSpecialProceed().
*/
void compileInvokeSpecial(ASTree target, String classname,
String methodname, String descriptor,
ASTList args)
throws CompileError
{
target.accept(this);
int nargs = getMethodArgsLength(args);
atMethodArgs(args, new int[nargs], new int[nargs],
new String[nargs]);
setReturnType(descriptor);
addNullIfVoid();
}
protected void compileUnwrapValue(CtClass type) throws CompileError
{
if (type == CtClass.voidType)
addNullIfVoid();
else
setType(type);
}
/* Sets exprType, arrayDim, and className;
* If type is void, then this method does nothing.
*/
public void setType(CtClass type) throws CompileError {
setType(type, 0);
}
private void setType(CtClass type, int dim) throws CompileError {
if (type.isPrimitive()) {
CtPrimitiveType pt = (CtPrimitiveType)type;
exprType = MemberResolver.descToType(pt.getDescriptor());
arrayDim = dim;
className = null;
}
else if (type.isArray())
try {
setType(type.getComponentType(), dim + 1);
}
catch (NotFoundException e) {
throw new CompileError("undefined type: " + type.getName());
}
else {
exprType = CLASS;
arrayDim = dim;
className = MemberResolver.javaToJvmName(type.getName());
}
}
}