001: // Copyright (c) 1999 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.expr;
005:
006: import gnu.bytecode.*;
007: import gnu.mapping.WrongType;
008:
009: /** Same as StackTarget, but catch ClassCastException.
010: * Generate code so that if coercion fails, catch ClassCastException,
011: * and re-throw a WrongType. This gives better error messages. */
012:
013: public class CheckedTarget extends StackTarget {
014: LambdaExp proc;
015: String procname;
016: /** 1-origin argument index,
017: or WrongType.ARG_CAST, or WrongType-ARG_VARNAME. */
018: int argno;
019:
020: public CheckedTarget(Type type) {
021: super (type);
022: argno = WrongType.ARG_CAST;
023: }
024:
025: public CheckedTarget(Type type, LambdaExp proc, int argno) {
026: super (type);
027: this .proc = proc;
028: this .procname = proc.getName();
029: this .argno = argno;
030: }
031:
032: public CheckedTarget(Type type, String procname, int argno) {
033: super (type);
034: this .procname = procname;
035: this .argno = argno;
036: }
037:
038: public static Target getInstance(Type type, String procname,
039: int argno) {
040: return (type == Type.pointer_type ? Target.pushObject
041: : new CheckedTarget(type, procname, argno));
042: }
043:
044: public static Target getInstance(Type type, LambdaExp proc,
045: int argno) {
046: return (type == Type.pointer_type ? Target.pushObject
047: : new CheckedTarget(type, proc, argno));
048: }
049:
050: public static Target getInstance(Type type) {
051: return (type == Type.pointer_type ? Target.pushObject
052: : new CheckedTarget(type));
053: }
054:
055: static ClassType typeClassCastException;
056: static ClassType typeWrongType;
057: static Method initWrongTypeStringMethod;
058: static Method initWrongTypeProcMethod;
059:
060: private static void initWrongType() {
061: if (typeClassCastException == null)
062: typeClassCastException = ClassType
063: .make("java.lang.ClassCastException");
064: if (typeWrongType == null) {
065: typeWrongType = ClassType.make("gnu.mapping.WrongType");
066: Type[] args = new Type[4];
067: args[0] = typeClassCastException;
068: args[1] = Compilation.javaStringType;
069: args[2] = Type.int_type;
070: args[3] = Type.pointer_type;
071: initWrongTypeStringMethod = typeWrongType.addMethod(
072: "<init>", Access.PUBLIC, args, Type.void_type);
073: args = new Type[4];
074: args[0] = typeClassCastException;
075: args[1] = Compilation.typeProcedure;
076: args[2] = Type.int_type;
077: args[3] = Type.pointer_type;
078: initWrongTypeProcMethod = typeWrongType.addMethod("<init>",
079: Access.PUBLIC, args, Type.void_type);
080: }
081: }
082:
083: public void compileFromStack(Compilation comp, Type stackType) {
084: if (!compileFromStack0(comp, stackType))
085: emitCheckedCoerce(comp, proc, procname, argno, type, null);
086: }
087:
088: public static void emitCheckedCoerce(Compilation comp,
089: String procname, int argno, Type type) {
090: emitCheckedCoerce(comp, null, procname, argno, type, null);
091: }
092:
093: public static void emitCheckedCoerce(Compilation comp,
094: LambdaExp proc, int argno, Type type) {
095: emitCheckedCoerce(comp, proc, proc.getName(), argno, type, null);
096: }
097:
098: public static void emitCheckedCoerce(Compilation comp,
099: LambdaExp proc, int argno, Type type, Variable argValue) {
100: emitCheckedCoerce(comp, proc, proc.getName(), argno, type,
101: argValue);
102: }
103:
104: static void emitCheckedCoerce(Compilation comp, LambdaExp proc,
105: String procname, int argno, Type type, Variable argValue) {
106: CodeAttr code = comp.getCode();
107: // If we're not in a try statement, it is more efficient to defer
108: // the handler to the end of the procedure (thus avoiding a goto,
109: // and potentially improving locality). If we're in a try statement,
110: // we can't safely do that
111: boolean isInTry = code.isInTry();
112: initWrongType();
113: Label startTry = new Label(code);
114: Scope tmpScope;
115: if (argValue == null && type != Type.tostring_type) {
116: tmpScope = code.pushScope();
117: argValue = code.addLocal(Type.pointer_type);
118: code.emitDup(1);
119: code.emitStore(argValue);
120: } else
121: tmpScope = null;
122: int startPC = code.getPC();
123: startTry.define(code);
124: emitCoerceFromObject(type, comp);
125:
126: int endPC = code.getPC();
127: // If no cast was needed, no code has been generated.
128: // Thus endPC is equal to startPC and we can stop safely.
129: // Also, tostring_type can never raise an exception, so we don't need
130: // to catch it.
131: if (endPC == startPC || type == Type.tostring_type) {
132: // FIXME should remove generated store to argValue, by truncating
133: // PC to startPC. But take care with startPC.position.
134: if (tmpScope != null)
135: code.popScope();
136: return;
137: }
138:
139: Label endTry = new Label(code);
140: endTry.define(code);
141:
142: Label endLabel = new Label(code);
143: if (isInTry)
144: code.emitGoto(endLabel);
145: int fragment_cookie = 0;
146: if (!isInTry)
147: fragment_cookie = code.beginFragment(new Label(code),
148: endLabel);
149: code.addHandler(startTry, endTry, typeClassCastException);
150: // Push arguments:
151: // ClassCastException is already pushed
152: code.pushType(typeClassCastException);
153: boolean this IsProc = false;
154: if (proc != null && proc.isClassGenerated()
155: && !comp.method.getStaticFlag()) {
156: if (comp.method.getDeclaringClass() == proc
157: .getCompiledClassType(comp))
158: this IsProc = true;
159: }
160: int line = comp.getLineNumber();
161: if (line > 0)
162: code.putLineNumber(line);
163: code.emitNew(typeWrongType);
164: code.emitDupX(); // dup_x1
165: code.emitSwap();
166: if (this IsProc)
167: code.emitPushThis();
168: else
169: code.emitPushString(procname == null
170: && argno != WrongType.ARG_CAST ? "lambda"
171: : procname);
172: code.emitPushInt(argno);
173: code.emitLoad(argValue);
174: code.emitInvokeSpecial(thisIsProc ? initWrongTypeProcMethod
175: : initWrongTypeStringMethod);
176: if (tmpScope != null)
177: code.popScope();
178: code.emitThrow();
179: if (isInTry)
180: endLabel.define(code);
181: else
182: code.endFragment(fragment_cookie);
183: }
184: }
|