001: /*=============================================================================
002: * Copyright Texas Instruments 2004. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package oscript.compiler;
020:
021: import oscript.syntaxtree.*;
022: import oscript.data.*;
023: import oscript.exceptions.*;
024: import oscript.util.OpenHashSymbolTable;
025:
026: // The Bytecode Engineerign Library
027: import org.apache.bcel.generic.*;
028: import org.apache.bcel.Constants;
029:
030: import java.util.Hashtable;
031: import java.util.LinkedList;
032: import java.util.Iterator;
033:
034: /**
035: * The syntax-tree (parse-tree) does not simply translate into a single
036: * <tt>NodeEvaluator</tt>. Instead, functions defined within the file,
037: * etc., also turn into "nested" <tt>NodeEvaluator</tt>s. Each nested
038: * <tt>NodeEvaluator</tt> results in construction of a new compiler
039: * (<tt>CompilerVisitor</tt>), which contributes an additional
040: * <tt>evalNode<i>X</i></tt> to the same classfile......
041: */
042: public class CompilerContext {
043: public static int failed = 0;
044: public static int succeeded = 0;
045:
046: public static final boolean DEBUG = false;
047:
048: /**
049: * Used to load a class from a <code>JavaClass</code>.
050: */
051: private static final CompilerClassLoader loader = CompilerClassLoader
052: .getCompilerClassLoader();
053:
054: /**
055: * The index of the last <tt>evalNode<i>X</i>()</tt> method.
056: */
057: private int lastEvalNodeIdx = -1;
058:
059: /**
060: * The names of the eval-node methods, indexed by eval-node-idx
061: */
062: private LinkedList evalNodeNameList = new LinkedList();
063:
064: /**
065: */
066: String name;
067:
068: /**
069: * The name of the class that is generated.
070: */
071: String className;
072:
073: /**
074: * The class that is being built.
075: */
076: ClassGen cg;
077:
078: /**
079: * The constant-pool of the class that is being built.
080: */
081: ConstantPoolGen cp;
082:
083: /**
084: * topmost compiler instance
085: */
086: CompilerVisitor cv;
087:
088: /**
089: */
090: private Hashtable smitIdxTable = new Hashtable();
091:
092: /**
093: * Maps constants (see <code>NodeToken</code> visit method) to a
094: * fieldref.
095: */
096: private Hashtable constFieldRefTable = new Hashtable();
097:
098: /**
099: * Maps symbol name to a fieldref.
100: */
101: private Hashtable symbolFieldRefTable = new Hashtable();
102:
103: /*=======================================================================*/
104: /**
105: * Note, conflict between org.apache.bcel.generic.Type and oscript.data.Type.
106: */
107: static final org.apache.bcel.generic.Type OBJECT_TYPE = new ObjectType(
108: "java.lang.Object");
109: static final org.apache.bcel.generic.Type STRING_TYPE = new ObjectType(
110: "java.lang.String");
111: static final org.apache.bcel.generic.Type SCOPE_TYPE = new ObjectType(
112: "oscript.data.Scope");
113: static final org.apache.bcel.generic.Type VALUE_TYPE = new ObjectType(
114: "oscript.data.Value");
115: static final org.apache.bcel.generic.Type SYMBOL_TABLE_TYPE = new ObjectType(
116: "oscript.util.SymbolTable");
117: static final org.apache.bcel.generic.Type VALUE_ARRAY_TYPE = new ArrayType(
118: VALUE_TYPE, 1);
119: static final ObjectType ANY_EXCEPTION_TYPE = new ObjectType(
120: "java.lang.Throwable");
121: static final ObjectType EXCEPTION_TYPE = new ObjectType(
122: "oscript.exceptions.PackagedScriptObjectException");
123:
124: static final org.apache.bcel.generic.Type[] EVAL_NODE_ARG_TYPES = new org.apache.bcel.generic.Type[] {
125: new ObjectType("oscript.util.StackFrame"), SCOPE_TYPE };
126: static final String[] EVAL_NODE_ARG_NAMES = new String[] { "sf",
127: "scope" };
128:
129: public static final int[] EMPTY_ARG_IDS = new int[0];
130:
131: /**
132: * The instructions for the "<clinit>" method.
133: */
134: private InstructionList clinitIl = new CompilerInstructionList();
135:
136: /**
137: * The instructions for the "<init>" method.
138: */
139: private InstructionList initIl = new CompilerInstructionList();
140:
141: /*=======================================================================*/
142: /**
143: * The entry-point to the compiler
144: */
145: static final CompiledNodeEvaluator compileNode(String name,
146: Node node) {
147: /* NOTE: BCEL is not thread safe, so synchronize use of the library on
148: * the ClassGen class... we do the same thing in ClassWrapGen
149: */
150: synchronized (ClassGen.class) {
151: return (new CompilerContext(name)).compileNodeImpl(node);
152: }
153: }
154:
155: /*=======================================================================*/
156: /**
157: * Class Constructor.
158: *
159: * @param name the name of the class to generate
160: */
161: private CompilerContext(String name) {
162: this .name = name;
163: this .className = loader
164: .makeClassName("_oscn"
165: + oscript.OscriptInterpreter.CACHE_VERSION
166: + "." + name);
167:
168: cg = new ClassGen(className,
169: "oscript.compiler.CompiledNodeEvaluator",
170: "<generated>", Constants.ACC_PUBLIC
171: | Constants.ACC_SUPER, new String[] {});
172:
173: cp = cg.getConstantPool();
174: }
175:
176: /*=======================================================================*/
177: /**
178: * Get the index used for the name of the next <tt>evalNode<i>X</i>()</tt>
179: * method. This is tracked here so an <tt>evalInnerNode</tt> switch method
180: * can be constructed with the appropriate number of cases
181: * <p>
182: * The generated method corresponding to this index, should have the name
183: * <tt>"_" + idx + "_" + name</tt>
184: */
185: int getNextEvalNodeIdx(String name) {
186: evalNodeNameList.add(name);
187: return ++lastEvalNodeIdx;
188: }
189:
190: /*=======================================================================*/
191: /**
192: * For each function, in addition to having an specific eval method,
193: * has a SMIT. The CompilerContext needs to know it's index, so it
194: * can generate an accessor method.
195: */
196: void addSMITs(int evalNodeIdx, int[] smitIdxs) {
197: smitIdxTable.put(new Integer(evalNodeIdx), smitIdxs);
198: }
199:
200: /*=======================================================================*/
201: /**
202: * The entry point to compile a node.
203: *
204: * @param node the node in syntaxtree to compile
205: */
206: private CompiledNodeEvaluator compileNodeImpl(Node node) {
207: try {
208: // invoke the compiler to generate the top-most node-evaluator,
209: // with inner node-evaluators are generated by recursively constructing
210: // CompilerVisitor-s
211: cv = new CompilerVisitor(this , "file", node);
212:
213: // build the constructor that takes an array of objects:
214: // these have to be done after we done with mg
215: dumpConstructor();
216: dumpEvalInnerNode();
217: dumpGetInnerSharedMemberIndexTable();
218: dumpInit();
219:
220: // XXX for debugging:
221: if (DEBUG) {
222: try {
223: org.apache.bcel.classfile.JavaClass c = cg
224: .getJavaClass();
225: String dir = "/tmp/oscn/"
226: + c.getPackageName().replace('.', '/')
227: + "/";
228: (new java.io.File(dir)).mkdirs();
229: new org.apache.bcel.util.Class2HTML(c, dir);
230: } catch (java.io.IOException e) {
231: e.printStackTrace();
232: }
233: }
234:
235: dumpMemberStats();
236:
237: Class c = loader.makeClass(className, cg.getJavaClass());
238: CompiledNodeEvaluator result = (CompiledNodeEvaluator) (c
239: .newInstance());
240: succeeded++;
241: return result;
242: } catch (LinkageError e) {
243: /* this means we hit a bug or limitation of the compiler:
244: *
245: * note: we can hit this for method bodies that are too big,
246: * so don't treat as fatal error
247: */
248: compileNodeException(e);
249: } catch (ClassGenException e) {
250: /* this means we hit a bug or limitation of the compiler:
251: */
252: compileNodeException(e);
253: } catch (Throwable e) {
254: // treat this as a more fatal sort of error than LinkageError
255: compileNodeException(e);
256: throw new ProgrammingErrorException(e);
257: }
258: return null;
259: }
260:
261: private void compileNodeException(Throwable e) {
262: String name = this .name;
263: if (cv != null)
264: name = cv.getDebugName();
265: System.err.println(failed + ":" + succeeded
266: + "\tError compiling " + name + ": " + e.getMessage());
267: if (DEBUG)
268: e.printStackTrace();
269: failed++;
270: }
271:
272: /**
273: * Dump no-arg constructor. Modifies <code>il</code> and <code>mg</code>
274: */
275: private final void dumpConstructor() {
276: InstructionList il = new CompilerInstructionList();
277:
278: MethodGen mg = new MethodGen(Constants.ACC_PUBLIC,
279: org.apache.bcel.generic.Type.VOID,
280: new org.apache.bcel.generic.Type[0], new String[0],
281: "<init>", className, il, cp);
282:
283: il.append(InstructionConstants.ALOAD_0);
284: il.append(new PUSH(cp, name));
285: il.append(new PUSH(cp, cv.getDesc()));
286: il.append(new INVOKESPECIAL(methodref(
287: "oscript.compiler.CompiledNodeEvaluator", "<init>",
288: "(Ljava/lang/String;Ljava/lang/String;)V")));
289:
290: il.append(initIl);
291: il.append(InstructionConstants.RETURN);
292:
293: mg.setMaxStack();
294: cg.addMethod(mg.getMethod());
295: }
296:
297: private final InstructionHandle dumpSwitchDefault(
298: InstructionList il, String msg) {
299: // il.append( new NEW( cp.addClass("oscript.exceptions.ProgrammingErrorException") ) );
300: // il.append( InstructionConstants.DUP );
301: // InstructionHandle defaultTarget = il.append( new PUSH( cp, msg ) );
302: // il.append(
303: // new INVOKESPECIAL(
304: // methodref(
305: // "oscript.exceptions.ProgrammingErrorException",
306: // "<init>",
307: // "(Ljava/lang/String;)V"
308: // )
309: // )
310: // );
311: // il.append( InstructionConstants.ATHROW )
312: InstructionHandle defaultTarget = il
313: .append(InstructionConstants.ACONST_NULL);
314: il.append(InstructionConstants.ARETURN);
315: return defaultTarget;
316: }
317:
318: private final void dumpEvalInnerNode() {
319: InstructionList il = new CompilerInstructionList();
320:
321: MethodGen mg = new MethodGen(Constants.ACC_PUBLIC
322: | Constants.ACC_FINAL, OBJECT_TYPE,
323: new org.apache.bcel.generic.Type[] {
324: org.apache.bcel.generic.Type.INT,
325: new ObjectType("oscript.util.StackFrame"),
326: SCOPE_TYPE }, new String[] { "idx", "sf",
327: "scope" }, "evalInnerNode", className, il, cp);
328:
329: int[] idxs = new int[lastEvalNodeIdx + 1];
330: InstructionHandle[] targets = new InstructionHandle[lastEvalNodeIdx + 1];
331: for (int i = 0; i <= lastEvalNodeIdx; i++) {
332: idxs[i] = i;
333:
334: targets[i] = il.append(InstructionConstants.ALOAD_0);
335: il.append(InstructionConstants.ALOAD_2);
336: il.append(new ALOAD(3));
337: il
338: .append(new INVOKEVIRTUAL(
339: methodref(className, "_" + i + "_"
340: + evalNodeNameList.get(i),
341: "(Loscript/util/StackFrame;Loscript/data/Scope;)Ljava/lang/Object;")));
342: il.append(InstructionConstants.ARETURN);
343: }
344:
345: // since we need to know the branch targets first, the switch is inserted
346: // at the head of the method (in reverse order):
347: il.insert(new TABLESWITCH(idxs, targets, dumpSwitchDefault(il,
348: "invalid idx")));
349: il.insert(InstructionConstants.ILOAD_1);
350:
351: mg.setMaxStack();
352: cg.addMethod(mg.getMethod());
353: }
354:
355: private final void dumpGetInnerSharedMemberIndexTable() {
356: InstructionList il = new CompilerInstructionList();
357:
358: MethodGen mg = new MethodGen(Constants.ACC_PUBLIC
359: | Constants.ACC_FINAL, SYMBOL_TABLE_TYPE,
360: new org.apache.bcel.generic.Type[] {
361: org.apache.bcel.generic.Type.INT,
362: org.apache.bcel.generic.Type.INT },
363: new String[] { "idx", "perm" },
364: "getInnerSharedMemberIndexTable", className, il, cp);
365:
366: int[] idxs = new int[lastEvalNodeIdx + 1];
367: InstructionHandle[] idxTargets = new InstructionHandle[lastEvalNodeIdx + 1];
368: for (int i = 0; i <= lastEvalNodeIdx; i++) {
369: InstructionList iil = new CompilerInstructionList();
370: idxs[i] = i;
371:
372: InstructionHandle[] permTargets = new InstructionHandle[oscript.NodeEvaluator.SMIT_PERMS.length];
373: for (int j = 0; j < oscript.NodeEvaluator.SMIT_PERMS.length; j++) {
374: permTargets[j] = iil
375: .append(new GETSTATIC(
376: ((int[]) (smitIdxTable.get(new Integer(
377: i))))[oscript.NodeEvaluator.SMIT_PERMS[j]]));
378: iil.append(InstructionConstants.ARETURN);
379: }
380:
381: // since we need to know the branch targets first, the switch is inserted
382: // at the head of the method (in reverse order):
383: iil.insert(new TABLESWITCH(
384: oscript.NodeEvaluator.SMIT_PERMS, permTargets,
385: dumpSwitchDefault(iil, "invalid perm")));
386: idxTargets[i] = iil.insert(InstructionConstants.ILOAD_2);
387: il.append(iil);
388: }
389:
390: // since we need to know the branch targets first, the switch is inserted
391: // at the head of the method (in reverse order):
392: il.insert(new TABLESWITCH(idxs, idxTargets, dumpSwitchDefault(
393: il, "invalid idx")));
394: il.insert(InstructionConstants.ILOAD_1);
395:
396: mg.setMaxStack();
397: cg.addMethod(mg.getMethod());
398: }
399:
400: private final void dumpInit() {
401: InstructionList il = clinitIl;
402:
403: MethodGen mg = new MethodGen(Constants.ACC_PUBLIC,
404: org.apache.bcel.generic.Type.VOID,
405: new org.apache.bcel.generic.Type[0], new String[0],
406: "<clinit>", className, il, cp);
407:
408: il.append(InstructionConstants.RETURN);
409:
410: mg.setMaxStack();
411: cg.addMethod(mg.getMethod());
412: }
413:
414: /*=======================================================================*/
415: static Class makeAccessible(Class c) {
416: // deal with classes that we don't have access to by using the
417: // classes parent type:
418: while (!java.lang.reflect.Modifier.isPublic(c.getModifiers()))
419: c = c.getSuperclass();
420:
421: return c;
422: }
423:
424: /*=======================================================================*/
425: int methodref(String className, String methodName, String sig) {
426: return cp.addMethodref(className, methodName, sig);
427: }
428:
429: int ifmethodref(String className, String methodName, String sig) {
430: return cp.addInterfaceMethodref(className, methodName, sig);
431: }
432:
433: int fieldref(String className, String methodName, String sig) {
434: return cp.addFieldref(className, methodName, sig);
435: }
436:
437: /*=======================================================================*/
438: private static int identifierCnt = 0;
439:
440: static String makeUniqueIdentifierName(String name) {
441: return name + Math.abs(++identifierCnt);
442: }
443:
444: /*=======================================================================*/
445: void pushInt(InstructionList il, int i) {
446: if ((i >= -1) && (i <= 5))
447: il.append(new ICONST(i));
448: else
449: il.append(new LDC(cp.addInteger(i)));
450: }
451:
452: /*=======================================================================*/
453: /**
454: */
455: int makeField(String name, Class cls) {
456: String fieldName = makeUniqueIdentifierName("c");
457:
458: org.apache.bcel.generic.Type type = getType(cls);
459:
460: FieldGen fg = new FieldGen(Constants.ACC_PRIVATE
461: | Constants.ACC_STATIC, type, fieldName, cp);
462:
463: cg.addField(fg.getField());
464:
465: return fieldref(className, fieldName, type.getSignature());
466: }
467:
468: /*=======================================================================*/
469: /**
470: * Get the instance variable index of the specified object. The first
471: * time this is called with a given object, this creates an member
472: * variable within the constructed class.
473: */
474: int getInstanceConstantIdx(Object obj) {
475: Integer iidx = (Integer) (constFieldRefTable.get(obj));
476:
477: // special case to handle the int[] argIds parameter for functions,
478: // since two identical arrays won't appear as equals() to the hashtable
479: if ((iidx == null) && (obj.getClass() == int[].class)) {
480: int[] arr = (int[]) obj;
481: for (Iterator itr = constFieldRefTable.keySet().iterator(); itr
482: .hasNext();) {
483: Object key = itr.next();
484: if ((key.getClass() == int[].class)
485: && java.util.Arrays.equals(arr, (int[]) key)) {
486: iidx = (Integer) (constFieldRefTable.get(key));
487: break;
488: }
489: }
490: }
491:
492: if (iidx == null) {
493: logMember(obj);
494:
495: Class c = obj.getClass();
496:
497: // hack to deal with RegExp:
498: if (obj instanceof RegExp)
499: c = RegExp.class;
500:
501: int idx = makeField("c", c);
502:
503: if (c == OString.class) {
504: clinitIl.append(new PUSH(cp, ((OString) obj)
505: .castToString()));
506: clinitIl.append(new INVOKESTATIC(methodref(
507: "oscript.data.OString", "makeString",
508: "(Ljava/lang/String;)Loscript/data/OString;")));
509: clinitIl.append(new PUTSTATIC(idx));
510: } else if (c == OExactNumber.class) {
511: clinitIl.append(new PUSH(cp, ((OExactNumber) obj)
512: .castToExactNumber()));
513: clinitIl.append(new INVOKESTATIC(methodref(
514: "oscript.data.OExactNumber", "makeExactNumber",
515: "(J)Loscript/data/OExactNumber;")));
516: clinitIl.append(new PUTSTATIC(idx));
517: } else if (c == OInexactNumber.class) {
518: clinitIl.append(new PUSH(cp, ((OInexactNumber) obj)
519: .castToInexactNumber()));
520: clinitIl.append(new INVOKESTATIC(methodref(
521: "oscript.data.OInexactNumber",
522: "makeInexactNumber",
523: "(D)Loscript/data/OInexactNumber;")));
524: clinitIl.append(new PUTSTATIC(idx));
525: } else if (c == RegExp.class) {
526: clinitIl.append(new PUSH(cp, ((RegExp) obj)
527: .castToString()));
528: clinitIl.append(new INVOKESTATIC(methodref(
529: "oscript.data.OString", "makeString",
530: "(Ljava/lang/String;)Loscript/data/OString;")));
531: clinitIl
532: .append(new INVOKESTATIC(
533: methodref("oscript.data.RegExp",
534: "createRegExp",
535: "(Loscript/data/Value;)Loscript/data/RegExp;")));
536: clinitIl.append(new PUTSTATIC(idx));
537: } else if (c == OpenHashSymbolTable.class) {
538: OpenHashSymbolTable st = (OpenHashSymbolTable) obj;
539: clinitIl.append(new NEW(cp
540: .addClass("oscript.util.OpenHashSymbolTable")));
541: clinitIl.append(InstructionConstants.DUP);
542: clinitIl.append(new PUSH(cp, st.size()));
543: clinitIl.append(new PUSH(cp, 0.75f));
544: clinitIl.append(new INVOKESPECIAL(methodref(
545: "oscript.util.OpenHashSymbolTable", "<init>",
546: "(IF)V")));
547: int[] idxs = new int[st.size()];
548: for (Iterator itr = st.symbols(); itr.hasNext();) {
549: int id = ((Integer) (itr.next())).intValue();
550: idxs[st.get(id)] = id;
551: }
552: for (int i = 0; i < idxs.length; i++) {
553: clinitIl.append(InstructionConstants.DUP);
554: pushSymbol(clinitIl, Symbol.getSymbol(idxs[i])
555: .castToString());
556: clinitIl.append(new INVOKEVIRTUAL(methodref(
557: "oscript.util.OpenHashSymbolTable",
558: "create", "(I)I")));
559: clinitIl.append(InstructionConstants.POP);
560: }
561: clinitIl.append(new PUTSTATIC(idx));
562: } else if (c == int[].class) {
563: int[] val = (int[]) obj;
564:
565: pushInt(clinitIl, val.length);
566: clinitIl.append(new NEWARRAY(
567: org.apache.bcel.generic.Type.INT));
568:
569: // array consists of [symbol,attr]*
570: for (int i = 0; i < val.length;) {
571: clinitIl.append(InstructionConstants.DUP);
572: pushInt(clinitIl, i);
573: pushSymbol(clinitIl, Symbol.getSymbol(val[i])
574: .castToString());
575: clinitIl.append(InstructionConstants.IASTORE);
576: i++;
577:
578: clinitIl.append(InstructionConstants.DUP);
579: pushInt(clinitIl, i);
580: pushInt(clinitIl, val[i]);
581: clinitIl.append(InstructionConstants.IASTORE);
582: i++;
583: }
584:
585: clinitIl.append(new PUTSTATIC(idx));
586: } else {
587: throw new ProgrammingErrorException(
588: "instance-constant: " + obj + "("
589: + obj.getClass().getName() + ")");
590: }
591:
592: constFieldRefTable.put(obj, iidx = new Integer(idx));
593: }
594:
595: return iidx.intValue();
596: }
597:
598: // for gathering statistics on members of the CompiledNodeEvaluator object:
599: // maps class -> use-count
600: private static Hashtable memberStatTable = null;
601: private static final boolean KEEP_MEMBER_STATS = false;
602:
603: private static final void logMember(Object obj) {
604: if (KEEP_MEMBER_STATS) {
605: if (memberStatTable == null)
606: memberStatTable = new Hashtable();
607:
608: MemberStat ms = (MemberStat) (memberStatTable.get(obj
609: .getClass()));
610:
611: if (ms == null)
612: memberStatTable.put(obj.getClass(),
613: ms = new MemberStat());
614:
615: ms.useCount++;
616: memberCount++;
617: }
618: }
619:
620: private static int memberCount = 0;
621:
622: private static final void dumpMemberStats() {
623: if (KEEP_MEMBER_STATS) {
624: System.err.println("Member Stats: (useCount increment: "
625: + memberCount + ")\n " + memberStatTable);
626: memberCount = 0;
627: }
628: }
629:
630: private static final class MemberStat {
631: int useCount = 0;
632:
633: public String toString() {
634: return "{ useCount=" + useCount + " }\n";
635: }
636: }
637:
638: /*=======================================================================*/
639: /**
640: * push the instance constant onto the stack, setting ret-val to true.
641: */
642: void pushInstanceConstant(InstructionList il, Object obj) {
643: // first check for null, no need to store that as an instance const:
644: if (obj == null) {
645: il.append(InstructionConstants.ACONST_NULL);
646: return;
647: }
648:
649: if (obj == Value.UNDEFINED)
650: il.append(new GETSTATIC(fieldref("oscript.data.Value",
651: "UNDEFINED", "Loscript/data/Value;")));
652: else if (obj == Value.NULL)
653: il.append(new GETSTATIC(fieldref("oscript.data.Value",
654: "NULL", "Loscript/data/Value;")));
655: else if (obj == OBoolean.TRUE)
656: il.append(new GETSTATIC(fieldref("oscript.data.OBoolean",
657: "TRUE", "Loscript/data/OBoolean;")));
658: else if (obj == OBoolean.FALSE)
659: il.append(new GETSTATIC(fieldref("oscript.data.OBoolean",
660: "FALSE", "Loscript/data/OBoolean;")));
661: else
662: il.append(new GETSTATIC(getInstanceConstantIdx(obj)));
663: }
664:
665: /*=======================================================================*/
666: /**
667: * dump a System.err.println() call, a debugging tool for compiled code
668: */
669: // private void dumpLog( InstructionList il, String str )
670: // {
671: // il.append( new GETSTATIC( fieldref( "java.lang.System", "err", "Ljava/io/PrintStream;" ) ) );
672: // il.append( new PUSH( cp, str ) );
673: // il.append( new INVOKEVIRTUAL( methodref( "java.io.PrintStream", "println", "(Ljava/lang/String;)V" ) ) );
674: // }
675: /*=======================================================================*/
676: /**
677: * as long as this is just a helper to pushFunctionData, no need to
678: * cache...
679: */
680: private void pushNodeEvaluator(InstructionList il, int id, int idx) {
681: if (idx == -1) {
682: il.append(InstructionConstants.ACONST_NULL);
683: return;
684: }
685:
686: il
687: .append(new NEW(
688: cp
689: .addClass("oscript.compiler.CompiledInnerNodeEvaluator")));
690: il.append(InstructionConstants.DUP);
691: pushSymbol(il, Symbol.getSymbol(id).castToString());
692: il.append(new PUSH(cp, idx));
693: il.append(InstructionConstants.ALOAD_0);
694: il.append(new INVOKESPECIAL(methodref(
695: "oscript.compiler.CompiledInnerNodeEvaluator",
696: "<init>",
697: "(IILoscript/compiler/CompiledNodeEvaluator;)V")));
698:
699: }
700:
701: /*=======================================================================*/
702: /**
703: * push FunctionData on the stack, but cached
704: */
705: void pushFunctionData(InstructionList il, int id, int[] argIds,
706: boolean varargs, int extendsIdx, int fxnIdx, int staticIdx,
707: boolean hasVarInScope, boolean hasFxnInScope, Value comment) {
708: int idx = makeField("fd", Function.FunctionData.class);
709:
710: initIl.append(new NEW(cp
711: .addClass("oscript.data.Function$FunctionData")));
712: initIl.append(InstructionConstants.DUP);
713: pushSymbol(initIl, Symbol.getSymbol(id).castToString());
714: pushInstanceConstant(initIl, argIds);
715: initIl.append(new PUSH(cp, varargs));
716: pushNodeEvaluator(initIl, id, extendsIdx);
717: pushNodeEvaluator(initIl, id, fxnIdx);
718: pushNodeEvaluator(initIl, id, staticIdx);
719: initIl.append(new PUSH(cp, hasVarInScope));
720: initIl.append(new PUSH(cp, hasFxnInScope));
721: pushInstanceConstant(initIl, comment);
722: initIl
723: .append(new INVOKESPECIAL(
724: methodref(
725: "oscript.data.Function$FunctionData",
726: "<init>",
727: "(I[IZLoscript/NodeEvaluator;Loscript/NodeEvaluator;Loscript/NodeEvaluator;ZZLoscript/data/Value;)V")));
728: initIl.append(new PUTSTATIC(idx));
729:
730: il.append(new GETSTATIC(idx));
731: }
732:
733: /*=======================================================================*/
734: /**
735: * Push symbol id (int) on stack. Note that the numeric value of the
736: * symbol may be different on differerent invocations of the script
737: * engine. In the case of <code>SymbolTable</code>s, this is taken
738: * into account when serializing, so when the table is re-created it
739: * is using the current numeric values of the symbol.
740: *
741: * @return the current symbol value (ie. with the current symbol table,
742: * but could be different when code is reloaded into a different
743: * invokation of the script env)
744: */
745: void pushSymbol(InstructionList il, String name) {
746: Integer iidx = (Integer) (symbolFieldRefTable.get(name));
747: if (iidx == null) {
748: int idx = makeField(name, int.class);
749: iidx = new Integer(idx);
750: clinitIl.append(new PUSH(cp, name));
751: clinitIl.append(new INVOKESTATIC(methodref(
752: "oscript.data.Symbol", "getSymbol",
753: "(Ljava/lang/String;)Loscript/data/Symbol;")));
754: clinitIl.append(new INVOKEVIRTUAL(methodref(
755: "oscript.data.Symbol", "getId", "()I")));
756: clinitIl.append(new PUTSTATIC(idx));
757: symbolFieldRefTable.put(name, iidx);
758: }
759: il.append(new GETSTATIC(iidx.intValue()));
760: }
761:
762: /*=======================================================================*/
763: static org.apache.bcel.generic.Type getType(Class c) {
764: if (c.isArray())
765: return new ArrayType(getType(c.getComponentType()), 1);
766: else if (c.isPrimitive())
767: return getPrimitiveType(c);
768: else
769: return new ObjectType(CompilerContext.makeAccessible(c)
770: .getName());
771: }
772:
773: /*=======================================================================*/
774: static org.apache.bcel.generic.Type getPrimitiveType(Class c) {
775: if (c == Boolean.TYPE)
776: return org.apache.bcel.generic.Type.BOOLEAN;
777: else if (c == Integer.TYPE)
778: return org.apache.bcel.generic.Type.INT;
779: else if (c == Short.TYPE)
780: return org.apache.bcel.generic.Type.SHORT;
781: else if (c == Byte.TYPE)
782: return org.apache.bcel.generic.Type.BYTE;
783: else if (c == Long.TYPE)
784: return org.apache.bcel.generic.Type.LONG;
785: else if (c == Double.TYPE)
786: return org.apache.bcel.generic.Type.DOUBLE;
787: else if (c == Float.TYPE)
788: return org.apache.bcel.generic.Type.FLOAT;
789: else if (c == Character.TYPE)
790: return org.apache.bcel.generic.Type.CHAR;
791: else
792: throw new ProgrammingErrorException("bad primitive type: "
793: + c);
794: }
795:
796: }
|