123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722 |
- /*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999- 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,
- * or the Apache License Version 2.0.
- *
- * 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.ClassPool;
- import javassist.CtClass;
- import javassist.CtPrimitiveType;
- import javassist.NotFoundException;
- import javassist.bytecode.Bytecode;
- import javassist.bytecode.Descriptor;
- import javassist.compiler.ast.ASTList;
- import javassist.compiler.ast.ASTree;
- import javassist.compiler.ast.CallExpr;
- import javassist.compiler.ast.CastExpr;
- import javassist.compiler.ast.Declarator;
- import javassist.compiler.ast.Expr;
- import javassist.compiler.ast.Member;
- import javassist.compiler.ast.Stmnt;
- import javassist.compiler.ast.Symbol;
-
- /* Code generator accepting extended Java syntax for Javassist.
- */
-
- public class JvstCodeGen extends MemberCodeGen {
- String paramArrayName = null;
- String paramListName = null;
- CtClass[] paramTypeList = null;
- private int paramVarBase = 0; // variable index for $0 or $1.
- private boolean useParam0 = false; // true if $0 is used.
- private String param0Type = null; // JVM name
- public static final String sigName = "$sig";
- public static final String dollarTypeName = "$type";
- public static final String clazzName = "$class";
- private CtClass dollarType = null;
- CtClass returnType = null;
- String returnCastName = null;
- @SuppressWarnings("unused")
- private String returnVarName = null; // null if $_ is not used.
- public static final String wrapperCastName = "$w";
- String proceedName = null;
- public static final String cflowName = "$cflow";
- ProceedHandler procHandler = null; // null if not used.
-
- public JvstCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
- super(b, cc, cp);
- setTypeChecker(new JvstTypeChecker(cc, cp, this));
- }
-
- /* Index of $1.
- */
- private int indexOfParam1() {
- return paramVarBase + (useParam0 ? 1 : 0);
- }
-
- /* Records a ProceedHandler obejct.
- *
- * @param name the name of the special method call.
- * it is usually $proceed.
- */
- public void setProceedHandler(ProceedHandler h, String name) {
- proceedName = name;
- procHandler = h;
- }
-
- /* If the type of the expression compiled last is void,
- * add ACONST_NULL and change exprType, arrayDim, className.
- */
- public void addNullIfVoid() {
- if (exprType == VOID) {
- bytecode.addOpcode(ACONST_NULL);
- exprType = CLASS;
- arrayDim = 0;
- className = jvmJavaLangObject;
- }
- }
-
- /* To support $args, $sig, and $type.
- * $args is an array of parameter list.
- */
- @Override
- public void atMember(Member mem) throws CompileError {
- String name = mem.get();
- if (name.equals(paramArrayName)) {
- compileParameterList(bytecode, paramTypeList, indexOfParam1());
- exprType = CLASS;
- arrayDim = 1;
- className = jvmJavaLangObject;
- }
- else if (name.equals(sigName)) {
- bytecode.addLdc(Descriptor.ofMethod(returnType, paramTypeList));
- bytecode.addInvokestatic("javassist/runtime/Desc", "getParams",
- "(Ljava/lang/String;)[Ljava/lang/Class;");
- exprType = CLASS;
- arrayDim = 1;
- className = "java/lang/Class";
- }
- else if (name.equals(dollarTypeName)) {
- if (dollarType == null)
- throw new CompileError(dollarTypeName + " is not available");
-
- bytecode.addLdc(Descriptor.of(dollarType));
- callGetType("getType");
- }
- else if (name.equals(clazzName)) {
- if (param0Type == null)
- throw new CompileError(clazzName + " is not available");
-
- bytecode.addLdc(param0Type);
- callGetType("getClazz");
- }
- else
- super.atMember(mem);
- }
-
- private void callGetType(String method) {
- bytecode.addInvokestatic("javassist/runtime/Desc", method,
- "(Ljava/lang/String;)Ljava/lang/Class;");
- exprType = CLASS;
- arrayDim = 0;
- className = "java/lang/Class";
- }
-
- @Override
- protected void atFieldAssign(Expr expr, int op, ASTree left,
- ASTree right, boolean doDup) throws CompileError
- {
- if (left instanceof Member
- && ((Member)left).get().equals(paramArrayName)) {
- if (op != '=')
- throw new CompileError("bad operator for " + paramArrayName);
-
- right.accept(this);
- if (arrayDim != 1 || exprType != CLASS)
- throw new CompileError("invalid type for " + paramArrayName);
-
- atAssignParamList(paramTypeList, bytecode);
- if (!doDup)
- bytecode.addOpcode(POP);
- }
- else
- super.atFieldAssign(expr, op, left, right, doDup);
- }
-
- protected void atAssignParamList(CtClass[] params, Bytecode code)
- throws CompileError
- {
- if (params == null)
- return;
-
- int varNo = indexOfParam1();
- int n = params.length;
- for (int i = 0; i < n; ++i) {
- code.addOpcode(DUP);
- code.addIconst(i);
- code.addOpcode(AALOAD);
- compileUnwrapValue(params[i], code);
- code.addStore(varNo, params[i]);
- varNo += is2word(exprType, arrayDim) ? 2 : 1;
- }
- }
-
- @Override
- 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(returnCastName)) {
- atCastToRtype(expr);
- return;
- }
- else if (typename.equals(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 {
- expr.getOprand().accept(this);
- if (exprType == VOID || isRefType(exprType) || arrayDim > 0)
- compileUnwrapValue(returnType, bytecode);
- else if (returnType instanceof CtPrimitiveType) {
- CtPrimitiveType pt = (CtPrimitiveType)returnType;
- int destType = MemberResolver.descToType(pt.getDescriptor());
- atNumCastExpr(exprType, destType);
- exprType = destType;
- arrayDim = 0;
- className = null;
- }
- else
- throw new CompileError("invalid cast");
- }
-
- protected void atCastToWrapper(CastExpr expr) throws CompileError {
- expr.getOprand().accept(this);
- if (isRefType(exprType) || arrayDim > 0)
- return; // Object type. do nothing.
-
- CtClass clazz = resolver.lookupClass(exprType, arrayDim, className);
- if (clazz instanceof CtPrimitiveType) {
- CtPrimitiveType pt = (CtPrimitiveType)clazz;
- String wrapper = pt.getWrapperName();
- bytecode.addNew(wrapper); // new <wrapper>
- bytecode.addOpcode(DUP); // dup
- if (pt.getDataSize() > 1)
- bytecode.addOpcode(DUP2_X2); // dup2_x2
- else
- bytecode.addOpcode(DUP2_X1); // dup2_x1
-
- bytecode.addOpcode(POP2); // pop2
- bytecode.addInvokespecial(wrapper, "<init>",
- "(" + pt.getDescriptor() + ")V");
- // invokespecial
- exprType = CLASS;
- arrayDim = 0;
- className = jvmJavaLangObject;
- }
- }
-
- /* Delegates to a ProcHandler object if the method call is
- * $proceed(). It may process $cflow().
- */
- @Override
- public void atCallExpr(CallExpr expr) throws CompileError {
- ASTree method = expr.oprand1();
- if (method instanceof Member) {
- String name = ((Member)method).get();
- if (procHandler != null && name.equals(proceedName)) {
- procHandler.doit(this, bytecode, (ASTList)expr.oprand2());
- return;
- }
- else if (name.equals(cflowName)) {
- atCflow((ASTList)expr.oprand2());
- return;
- }
- }
-
- super.atCallExpr(expr);
- }
-
- /* To support $cflow().
- */
- protected void atCflow(ASTList cname) throws CompileError {
- StringBuilder sbuf = new StringBuilder();
- if (cname == null || cname.tail() != null)
- throw new CompileError("bad " + cflowName);
-
- makeCflowName(sbuf, cname.head());
- String name = sbuf.toString();
- Object[] names = resolver.getClassPool().lookupCflow(name);
- if (names == null)
- throw new CompileError("no such " + cflowName + ": " + name);
-
- bytecode.addGetstatic((String)names[0], (String)names[1],
- "Ljavassist/runtime/Cflow;");
- bytecode.addInvokevirtual("javassist.runtime.Cflow",
- "value", "()I");
- exprType = INT;
- arrayDim = 0;
- className = null;
- }
-
- /* Syntax:
- *
- * <cflow> : $cflow '(' <cflow name> ')'
- * <cflow name> : <identifier> ('.' <identifier>)*
- */
- private static void makeCflowName(StringBuilder sbuf, ASTree name)
- throws CompileError
- {
- if (name instanceof Symbol) {
- sbuf.append(((Symbol)name).get());
- return;
- }
- else if (name instanceof Expr) {
- Expr expr = (Expr)name;
- if (expr.getOperator() == '.') {
- makeCflowName(sbuf, expr.oprand1());
- sbuf.append('.');
- makeCflowName(sbuf, expr.oprand2());
- return;
- }
- }
-
- throw new CompileError("bad " + cflowName);
- }
-
- /* 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 (paramTypeList != null
- && args != null && args.tail() == null) {
- ASTree left = args.head();
- return (left instanceof Member
- && ((Member)left).get().equals(paramListName));
- }
- return false;
- }
-
- /*
- public int getMethodArgsLength(ASTList args) {
- if (!isParamListName(args))
- return super.getMethodArgsLength(args);
-
- return paramTypeList.length;
- }
- */
-
- @Override
- public int getMethodArgsLength(ASTList args) {
- String pname = paramListName;
- int n = 0;
- while (args != null) {
- ASTree a = args.head();
- if (a instanceof Member && ((Member)a).get().equals(pname)) {
- if (paramTypeList != null)
- n += paramTypeList.length;
- }
- else
- ++n;
-
- args = args.tail();
- }
-
- return n;
- }
-
- @Override
- public void atMethodArgs(ASTList args, int[] types, int[] dims,
- String[] cnames) throws CompileError {
- CtClass[] params = paramTypeList;
- String pname = 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;
- int regno = indexOfParam1();
- for (int k = 0; k < n; ++k) {
- CtClass p = params[k];
- regno += bytecode.addLoad(regno, p);
- 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();
- }
- }
-
- /*
- public void atMethodArgs(ASTList args, int[] types, int[] dims,
- String[] cnames) throws CompileError {
- if (!isParamListName(args)) {
- super.atMethodArgs(args, types, dims, cnames);
- return;
- }
-
- CtClass[] params = paramTypeList;
- if (params == null)
- return;
-
- int n = params.length;
- int regno = indexOfParam1();
- for (int i = 0; i < n; ++i) {
- CtClass p = params[i];
- regno += bytecode.addLoad(regno, p);
- setType(p);
- types[i] = exprType;
- dims[i] = arrayDim;
- cnames[i] = className;
- }
- }
- */
-
- /* called by Javac#recordSpecialProceed().
- */
- void compileInvokeSpecial(ASTree target, int methodIndex,
- String descriptor, ASTList args)
- throws CompileError
- {
- target.accept(this);
- int nargs = getMethodArgsLength(args);
- atMethodArgs(args, new int[nargs], new int[nargs],
- new String[nargs]);
- bytecode.addInvokespecial(methodIndex, descriptor);
- setReturnType(descriptor, false, false);
- addNullIfVoid();
- }
-
- /*
- * Makes it valid to write "return <expr>;" for a void method.
- */
- @Override
- protected void atReturnStmnt(Stmnt st) throws CompileError {
- ASTree result = st.getLeft();
- if (result != null && returnType == CtClass.voidType) {
- compileExpr(result);
- if (is2word(exprType, arrayDim))
- bytecode.addOpcode(POP2);
- else if (exprType != VOID)
- bytecode.addOpcode(POP);
-
- result = null;
- }
-
- atReturnStmnt2(result);
- }
-
- /**
- * Makes a cast to the return type ($r) available.
- * It also enables $_.
- *
- * <p>If the return type is void, ($r) does nothing.
- * The type of $_ is java.lang.Object.
- *
- * @param resultName null if $_ is not used.
- * @return -1 or the variable index assigned to $_.
- */
- public int recordReturnType(CtClass type, String castName,
- String resultName, SymbolTable tbl) throws CompileError
- {
- returnType = type;
- returnCastName = castName;
- returnVarName = resultName;
- if (resultName == null)
- return -1;
- int varNo = getMaxLocals();
- int locals = varNo + recordVar(type, resultName, varNo, tbl);
- setMaxLocals(locals);
- return varNo;
- }
-
- /**
- * Makes $type available.
- */
- public void recordType(CtClass t) {
- dollarType = t;
- }
-
- /**
- * Makes method parameters $0, $1, ..., $args, $$, and $class available.
- * $0 is equivalent to THIS if the method is not static. Otherwise,
- * if the method is static, then $0 is not available.
- */
- public int recordParams(CtClass[] params, boolean isStatic,
- String prefix, String paramVarName,
- String paramsName, SymbolTable tbl)
- throws CompileError
- {
- return recordParams(params, isStatic, prefix, paramVarName,
- paramsName, !isStatic, 0, getThisName(), tbl);
- }
-
- /**
- * Makes method parameters $0, $1, ..., $args, $$, and $class available.
- * $0 is available only if use0 is true. It might not be equivalent
- * to THIS.
- *
- * @param params the parameter types (the types of $1, $2, ..)
- * @param prefix it must be "$" (the first letter of $0, $1, ...)
- * @param paramVarName it must be "$args"
- * @param paramsName it must be "$$"
- * @param use0 true if $0 is used.
- * @param paramBase the register number of $0 (use0 is true)
- * or $1 (otherwise).
- * @param target the class of $0. If use0 is false, target
- * can be null. The value of "target" is also used
- * as the name of the type represented by $class.
- * @param isStatic true if the method in which the compiled bytecode
- * is embedded is static.
- */
- public int recordParams(CtClass[] params, boolean isStatic,
- String prefix, String paramVarName,
- String paramsName, boolean use0,
- int paramBase, String target,
- SymbolTable tbl)
- throws CompileError
- {
- int varNo;
-
- paramTypeList = params;
- paramArrayName = paramVarName;
- paramListName = paramsName;
- paramVarBase = paramBase;
- useParam0 = use0;
-
- if (target != null)
- param0Type = MemberResolver.jvmToJavaName(target);
-
- inStaticMethod = isStatic;
- varNo = paramBase;
- if (use0) {
- String varName = prefix + "0";
- Declarator decl
- = new Declarator(CLASS, MemberResolver.javaToJvmName(target),
- 0, varNo++, new Symbol(varName));
- tbl.append(varName, decl);
- }
-
- for (int i = 0; i < params.length; ++i)
- varNo += recordVar(params[i], prefix + (i + 1), varNo, tbl);
-
- if (getMaxLocals() < varNo)
- setMaxLocals(varNo);
-
- return varNo;
- }
-
- /**
- * Makes the given variable name available.
- *
- * @param type variable type
- * @param varName variable name
- */
- public int recordVariable(CtClass type, String varName, SymbolTable tbl)
- throws CompileError
- {
- if (varName == null)
- return -1;
- int varNo = getMaxLocals();
- int locals = varNo + recordVar(type, varName, varNo, tbl);
- setMaxLocals(locals);
- return varNo;
- }
-
- private int recordVar(CtClass cc, String varName, int varNo,
- SymbolTable tbl) throws CompileError
- {
- if (cc == CtClass.voidType) {
- exprType = CLASS;
- arrayDim = 0;
- className = jvmJavaLangObject;
- }
- else
- setType(cc);
-
- Declarator decl
- = new Declarator(exprType, className, arrayDim,
- varNo, new Symbol(varName));
- tbl.append(varName, decl);
- return is2word(exprType, arrayDim) ? 2 : 1;
- }
-
- /**
- * Makes the given variable name available.
- *
- * @param typeDesc the type descriptor of the variable
- * @param varName variable name
- * @param varNo an index into the local variable array
- */
- public void recordVariable(String typeDesc, String varName, int varNo,
- SymbolTable tbl) throws CompileError
- {
- char c;
- int dim = 0;
- while ((c = typeDesc.charAt(dim)) == '[')
- ++dim;
-
- int type = MemberResolver.descToType(c);
- String cname = null;
- if (type == CLASS) {
- if (dim == 0)
- cname = typeDesc.substring(1, typeDesc.length() - 1);
- else
- cname = typeDesc.substring(dim + 1, typeDesc.length() - 1);
- }
-
- Declarator decl
- = new Declarator(type, cname, dim, varNo, new Symbol(varName));
- tbl.append(varName, decl);
- }
-
- /* compileParameterList() returns the stack size used
- * by the produced code.
- *
- * This method correctly computes the max_stack value.
- *
- * @param regno the index of the local variable in which
- * the first argument is received.
- * (0: static method, 1: regular method.)
- */
- public static int compileParameterList(Bytecode code,
- CtClass[] params, int regno) {
- if (params == null) {
- code.addIconst(0); // iconst_0
- code.addAnewarray(javaLangObject); // anewarray Object
- return 1;
- }
- CtClass[] args = new CtClass[1];
- int n = params.length;
- code.addIconst(n); // iconst_<n>
- code.addAnewarray(javaLangObject); // anewarray Object
- for (int i = 0; i < n; ++i) {
- code.addOpcode(Bytecode.DUP); // dup
- code.addIconst(i); // iconst_<i>
- if (params[i].isPrimitive()) {
- CtPrimitiveType pt = (CtPrimitiveType)params[i];
- String wrapper = pt.getWrapperName();
- code.addNew(wrapper); // new <wrapper>
- code.addOpcode(Bytecode.DUP); // dup
- int s = code.addLoad(regno, pt); // ?load <regno>
- regno += s;
- args[0] = pt;
- code.addInvokespecial(wrapper, "<init>",
- Descriptor.ofMethod(CtClass.voidType, args));
- // invokespecial
- }
- else {
- code.addAload(regno); // aload <regno>
- ++regno;
- }
-
- code.addOpcode(Bytecode.AASTORE); // aastore
- }
-
- return 8;
- }
-
- protected void compileUnwrapValue(CtClass type, Bytecode code)
- throws CompileError
- {
- if (type == CtClass.voidType) {
- addNullIfVoid();
- return;
- }
-
- if (exprType == VOID)
- throw new CompileError("invalid type for " + returnCastName);
-
- if (type instanceof CtPrimitiveType) {
- CtPrimitiveType pt = (CtPrimitiveType)type;
- // pt is not voidType.
- String wrapper = pt.getWrapperName();
- code.addCheckcast(wrapper);
- code.addInvokevirtual(wrapper, pt.getGetMethodName(),
- pt.getGetMethodDescriptor());
- setType(type);
- }
- else {
- code.addCheckcast(type);
- 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());
- }
- }
-
- /* Performs implicit coercion from exprType to type.
- */
- public void doNumCast(CtClass type) throws CompileError {
- if (arrayDim == 0 && !isRefType(exprType))
- if (type instanceof CtPrimitiveType) {
- CtPrimitiveType pt = (CtPrimitiveType)type;
- atNumCastExpr(exprType,
- MemberResolver.descToType(pt.getDescriptor()));
- }
- else
- throw new CompileError("type mismatch");
- }
- }
|