0001: /*
0002: * Copyright (c) 2005 Advanced Micro Devices, Inc.
0003: *
0004: * See the file "license.amd" for information on usage and
0005: * redistribution of this file, and for a DISCLAIMER OF ALL
0006: * WARRANTIES.
0007: *
0008: * RCS: @(#) $Id: TJC.java,v 1.35 2006/08/21 21:41:13 mdejong Exp $ *
0009: */
0010:
0011: // Runtime support for TJC compiler implementation.
0012: package tcl.lang;
0013:
0014: import java.util.HashMap;
0015: import java.util.Arrays;
0016:
0017: public class TJC {
0018:
0019: // Constants
0020:
0021: public static final int SWITCH_MODE_EXACT = 0;
0022: public static final int SWITCH_MODE_GLOB = 1;
0023: public static final int SWITCH_MODE_REGEXP = 2;
0024:
0025: public static final int EXPR_OP_MULT = Expression.MULT;
0026: public static final int EXPR_OP_DIVIDE = Expression.DIVIDE;
0027: public static final int EXPR_OP_MOD = Expression.MOD;
0028: public static final int EXPR_OP_PLUS = Expression.PLUS;
0029: public static final int EXPR_OP_MINUS = Expression.MINUS;
0030: public static final int EXPR_OP_LEFT_SHIFT = Expression.LEFT_SHIFT;
0031: public static final int EXPR_OP_RIGHT_SHIFT = Expression.RIGHT_SHIFT;
0032: public static final int EXPR_OP_LESS = Expression.LESS;
0033: public static final int EXPR_OP_GREATER = Expression.GREATER;
0034: public static final int EXPR_OP_LEQ = Expression.LEQ;
0035: public static final int EXPR_OP_GEQ = Expression.GEQ;
0036: public static final int EXPR_OP_EQUAL = Expression.EQUAL;
0037: public static final int EXPR_OP_NEQ = Expression.NEQ;
0038: public static final int EXPR_OP_BIT_AND = Expression.BIT_AND;
0039: public static final int EXPR_OP_BIT_XOR = Expression.BIT_XOR;
0040: public static final int EXPR_OP_BIT_OR = Expression.BIT_OR;
0041: public static final int EXPR_OP_STREQ = Expression.STREQ;
0042: public static final int EXPR_OP_STRNEQ = Expression.STRNEQ;
0043:
0044: public static final int EXPR_OP_UNARY_MINUS = Expression.UNARY_MINUS;
0045: public static final int EXPR_OP_UNARY_PLUS = Expression.UNARY_PLUS;
0046: public static final int EXPR_OP_UNARY_NOT = Expression.NOT;
0047: public static final int EXPR_OP_UNARY_BIT_NOT = Expression.BIT_NOT;
0048:
0049: public static final WrappedCommand INVALID_COMMAND_CACHE;
0050:
0051: static {
0052: // Setup a fake command wrapper. The only
0053: // point of this is to have a wrapper with
0054: // and invalid cmdEpoch that will never be
0055: // equal to a valid cmdEpoch for a command.
0056:
0057: INVALID_COMMAND_CACHE = new WrappedCommand();
0058: INVALID_COMMAND_CACHE.deleted = true;
0059: INVALID_COMMAND_CACHE.cmdEpoch = -1;
0060: }
0061:
0062: // Invoked to create and push a new CallFrame for local
0063: // variables when a command begins to execute. This
0064: // method does not assign method arguments to local
0065: // variables inside the CallFrame. The logic here is
0066: // copied from CallFrame.chain().
0067:
0068: public static CallFrame pushLocalCallFrame(Interp interp,
0069: Namespace ns) {
0070: CallFrame frame = interp.newCallFrame();
0071:
0072: // Namespace the command is defined in, default to global namespace
0073:
0074: if (ns != null) {
0075: frame.ns = ns;
0076: }
0077:
0078: // ignore objv
0079:
0080: // isProcCallFrame should be true
0081: if (frame.isProcCallFrame == false) {
0082: throw new TclRuntimeError(
0083: "expected isProcCallFrame to be true");
0084: }
0085:
0086: frame.level = (interp.varFrame == null) ? 1
0087: : (interp.varFrame.level + 1);
0088: frame.caller = interp.frame;
0089: frame.callerVar = interp.varFrame;
0090: interp.frame = frame;
0091: interp.varFrame = frame;
0092:
0093: return frame;
0094: }
0095:
0096: // Invoked to pop a local CallFrame when a command is finished.
0097:
0098: public static void popLocalCallFrame(Interp interp, CallFrame frame) {
0099: // Cleanup code copied from Procedure.java. See the cmdProc
0100: // implementation in that class for more info.
0101:
0102: if (interp.errInProgress) {
0103: frame.dispose();
0104: interp.errInProgress = true;
0105: } else {
0106: frame.dispose();
0107: }
0108: }
0109:
0110: // Invoked to add a compiledLocals array to a
0111: // CallFrame that was just pushed. This
0112: // compiledLocals array is disposed of
0113: // automatically when the CallFrame is popped.
0114:
0115: public static Var[] initCompiledLocals(final CallFrame frame,
0116: final int size, final String[] names) {
0117: frame.compiledLocalsNames = names;
0118: return frame.compiledLocals = new Var[size];
0119: }
0120:
0121: // Evaluate a Tcl string that is the body of a Tcl procedure.
0122:
0123: public static void evalProcBody(Interp interp, String body)
0124: throws TclException {
0125: interp.eval(body);
0126: }
0127:
0128: // Check a TclException raised while a compiled Tcl procedure
0129: // is executing. This method should be invoked from a catch
0130: // block around the body code for a proc. This method could
0131: // handle the exception and stop if from propagating in the
0132: // case of a return, or it could allow it to propagate for
0133: // a normal error case. The logic in this method is copied
0134: // from Procedure.cmdProc().
0135:
0136: public static void checkTclException(Interp interp, TclException e,
0137: String procName) throws TclException {
0138: // FIXME: Ugh this is nasty. How can TCL.OK be package private if this
0139: // kind of mess could appear in the result from any eval()? All of this
0140: // should be in an interp method to handle these cases.
0141:
0142: int code = e.getCompletionCode();
0143: if (code == TCL.RETURN) {
0144: int realCode = interp.updateReturnInfo();
0145: if (realCode != TCL.OK) {
0146: e.setCompletionCode(realCode);
0147: throw e;
0148: }
0149: } else if (code == TCL.ERROR) {
0150: interp.addErrorInfo("\n (procedure \"" + procName
0151: + "\" line 1)");
0152: throw e;
0153: } else if (code == TCL.BREAK) {
0154: throw new TclException(interp,
0155: "invoked \"break\" outside of a loop");
0156: } else if (code == TCL.CONTINUE) {
0157: throw new TclException(interp,
0158: "invoked \"continue\" outside of a loop");
0159: } else {
0160: throw e;
0161: }
0162: }
0163:
0164: // Base class for TJC compiled commands
0165:
0166: public static abstract class CompiledCommand implements Command {
0167: public WrappedCommand wcmd = null;
0168:
0169: // A CompiledCommand implementation must define cmdProc
0170:
0171: // This flag is used to indicate that the command was
0172: // compiled with inlined Tcl commands.
0173:
0174: protected boolean inlineCmds = false;
0175:
0176: // A CompiledCommand implementation may want to init
0177: // instance data members when first invoked. This
0178: // flag and method are used to implement this init
0179: // check.
0180:
0181: protected boolean initCmd = false;
0182:
0183: protected void initCmd(Interp interp) throws TclException {
0184: if (initCmd) {
0185: throw new TclRuntimeError("initCmd already invoked");
0186: }
0187: builtinCommandsCheck(interp);
0188: initConstants(interp);
0189: initCmd = true;
0190: }
0191:
0192: // The following method should be defined in a specific
0193: // command implementations to init constant instance data.
0194:
0195: protected void initConstants(Interp interp) throws TclException {
0196: }
0197:
0198: // Verify that this compiled command is not being used
0199: // in a namespace that contains commands with the same
0200: // name Tcl commands that could have been inlined.
0201: // Since this check is only done when the command is
0202: // first invoked, it should not be a performance concern.
0203:
0204: protected void builtinCommandsCheck(Interp interp)
0205: throws TclException {
0206: if (wcmd.ns.fullName.equals("::")) {
0207: return; // loaded into global namespace
0208: }
0209: String[] containers = { "break", "catch", "continue",
0210: "expr", "for", "foreach", "if", "return", "switch",
0211: "while" };
0212: String[] containers_and_inlines = { "break", "catch",
0213: "continue", "expr", "for", "foreach", "global",
0214: "if", "list", "llength", "return", "set", "switch",
0215: "while" };
0216: String[] builtin = (inlineCmds ? containers_and_inlines
0217: : containers);
0218: WrappedCommand cmd;
0219: String cmdName;
0220: for (int i = 0; i < builtin.length; i++) {
0221: cmdName = builtin[i];
0222: cmd = Namespace.findCommand(interp, cmdName, wcmd.ns,
0223: TCL.NAMESPACE_ONLY);
0224: if (cmd != null) {
0225: throw new TclException(
0226: interp,
0227: "TJC compiled command"
0228: + " can't be loaded into the namespace "
0229: + wcmd.ns.fullName
0230: + " as it defines the builtin Tcl command \""
0231: + cmdName + "\" (" + cmd.toString()
0232: + ")");
0233: }
0234: }
0235: }
0236:
0237: // The following methods are used in compiled commands
0238: // that make use of cached variable access.
0239:
0240: // initVarScoped() is invoked for a scoped variable
0241: // like "::myglobal". This method will create a local
0242: // var linked to a variable defined in another scope
0243: // if the compiled local has not been initialized yet.
0244:
0245: protected final void initVarScoped(final Interp interp,
0246: final String varname, // Fully qualified varname
0247: // including namespace scope.
0248: // Can be array or scalar.
0249: final Var[] compiledLocals, final int localIndex)
0250: throws TclException {
0251: if (compiledLocals[localIndex] == null) {
0252: // Passing EXPLICIT_LOCAL_NAME tells makeUpvar
0253: // to do nothing when varname lookup fails.
0254: Var.makeUpvar(interp, null, varname, null,
0255: TCL.GLOBAL_ONLY, varname,
0256: Var.EXPLICIT_LOCAL_NAME, localIndex);
0257: }
0258: }
0259:
0260: // getVarScalar() will get a variable value, if a
0261: // cached variable is available then it will be used.
0262: // Otherwise the runtime getVar() will be invoked to get
0263: // the value. This method will raise a TclException
0264: // on error, it will never return null.
0265:
0266: protected final TclObject getVarScalar(final Interp interp,
0267: final String varname, // Scalar variable name
0268: final Var[] compiledLocals, final int localIndex)
0269: throws TclException {
0270: Var var = compiledLocals[localIndex];
0271:
0272: if ((var == null)
0273: || ((var = Var.resolveScalar(var)) == null)) {
0274: return Var.getVarCompiledLocalScalarInvalid(interp,
0275: varname);
0276: } else {
0277: return var.tobj;
0278: }
0279: }
0280:
0281: // getVarArray() will get an array element value,
0282: // if a cached variable is available, then it will be used.
0283: // Otherwise the runtime getVar() will be invoked to get
0284: // the value. This method will raise a TclException
0285: // on error, it will never return null.
0286:
0287: protected final TclObject getVarArray(final Interp interp,
0288: final String varname, // Array variable name
0289: final String key, // Array key
0290: final Var[] compiledLocals, final int localIndex)
0291: throws TclException {
0292: Var var = compiledLocals[localIndex];
0293:
0294: if (var == null || ((var = Var.resolveArray(var)) == null)) {
0295: return Var.getVarCompiledLocalArrayInvalid(interp,
0296: varname, key);
0297: } else {
0298: return Var.getVarCompiledLocalArray(interp, varname,
0299: key, var, true);
0300: }
0301: }
0302:
0303: // setVarScalar() will set a scalar variable value,
0304: // if a cached variable is available then it will be used,
0305: // otherwise the runtime setVar() will be invoked to set
0306: // the value. This method will raise a TclException
0307: // on error, it will never return null.
0308:
0309: protected final TclObject setVarScalar(final Interp interp,
0310: final String varname, // Scalar variable name
0311: final TclObject value, // New variable value
0312: final Var[] compiledLocals, final int localIndex)
0313: throws TclException {
0314: Var var = compiledLocals[localIndex];
0315:
0316: if (var == null) {
0317: return Var.initVarCompiledLocalScalar(interp, varname,
0318: value, compiledLocals, localIndex);
0319: } else if ((var = Var.resolveScalar(var)) == null) {
0320: return Var.setVarCompiledLocalScalarInvalid(interp,
0321: varname, value);
0322: } else {
0323: TJC.setVarScalar(var, value);
0324: return value;
0325: }
0326: }
0327:
0328: protected final TclObject setVarScalar(final Interp interp,
0329: final String varname, final String value,
0330: final Var[] compiledLocals, final int localIndex)
0331: throws TclException {
0332: TclObject tobj = interp.checkCommonString(value);
0333: return setVarScalar(interp, varname, tobj, compiledLocals,
0334: localIndex);
0335: }
0336:
0337: // setVarArray() will set an array element to a value,
0338: // if a cached variable is available then it will be used,
0339: // otherwise the runtime setVar() will be invoked to set
0340: // the value. This method will raise a TclException
0341: // on error, it will never return null.
0342:
0343: protected final TclObject setVarArray(final Interp interp,
0344: final String varname, final String key,
0345: final TclObject value, final Var[] compiledLocals,
0346: final int localIndex) throws TclException {
0347: Var var = compiledLocals[localIndex];
0348:
0349: if (var == null) {
0350: return Var.initVarCompiledLocalArray(interp, varname,
0351: key, value, compiledLocals, localIndex);
0352: } else if ((var = Var.resolveArray(var)) == null) {
0353: return Var.setVarCompiledLocalArrayInvalid(interp,
0354: varname, key, value);
0355: } else {
0356: return Var.setVarCompiledLocalArray(interp, varname,
0357: key, value, var);
0358: }
0359: }
0360:
0361: protected final TclObject setVarArray(final Interp interp,
0362: final String varname, final String key,
0363: final String value, final Var[] compiledLocals,
0364: final int localIndex) throws TclException {
0365: TclObject tobj = interp.checkCommonString(value);
0366: return setVarArray(interp, varname, key, tobj,
0367: compiledLocals, localIndex);
0368: }
0369:
0370: // incrVarScalar() will increment a scalar compiled local
0371: // by the given incrAmount. If the variable is not a
0372: // scalar local or has traces set then the runtime version
0373: // of the incr command will be used.
0374:
0375: protected final TclObject incrVarScalar(final Interp interp,
0376: final String varname, final int incrAmount,
0377: final Var[] compiledLocals, final int localIndex)
0378: throws TclException {
0379: Var var = compiledLocals[localIndex];
0380:
0381: if (var == null || ((var = Var.resolveScalar(var)) == null)) {
0382: return TJC.incrVar(interp, varname, null, incrAmount);
0383: } else {
0384: TclObject varValue = var.tobj;
0385:
0386: boolean createdNewObj = false;
0387: if (varValue.isShared()) {
0388: varValue = varValue.duplicate();
0389: createdNewObj = true;
0390: }
0391: try {
0392: TclInteger.incr(interp, varValue, incrAmount);
0393: } catch (TclException ex) {
0394: if (createdNewObj) {
0395: varValue.release(); // free unneeded copy
0396: }
0397: throw ex;
0398: }
0399:
0400: // If we create a new TclObject, then save it
0401: // as the variable value.
0402:
0403: if (createdNewObj) {
0404: TJC.setVarScalar(var, varValue);
0405: }
0406: return varValue;
0407: }
0408: }
0409:
0410: // incrVarArray() will increment an array element.
0411: // If the array is not a compiled local scalar then
0412: // the runtime implementation will be used.
0413:
0414: protected final TclObject incrVarArray(final Interp interp,
0415: final String varname, final String key,
0416: final int incrAmount, final Var[] compiledLocals,
0417: final int localIndex) throws TclException {
0418: Var var = compiledLocals[localIndex];
0419:
0420: if (var == null || ((var = Var.resolveArray(var)) == null)) {
0421: return TJC.incrVar(interp, varname, key, incrAmount);
0422: } else {
0423: TclObject varValue = Var.getVarCompiledLocalArray(
0424: interp, varname, key, var, true);
0425:
0426: boolean createdNewObj = false;
0427: if (varValue.isShared()) {
0428: varValue = varValue.duplicate();
0429: createdNewObj = true;
0430: }
0431: try {
0432: TclInteger.incr(interp, varValue, incrAmount);
0433: } catch (TclException ex) {
0434: if (createdNewObj) {
0435: varValue.release(); // free unneeded copy
0436: }
0437: throw ex;
0438: }
0439:
0440: // Set the array element once again since the
0441: // variable could have traces.
0442:
0443: return Var.setVarCompiledLocalArray(interp, varname,
0444: key, varValue, var);
0445: }
0446: }
0447:
0448: // lappendVarScalar() will append list elements to
0449: // a scalar local variable. If the variable is not a
0450: // scalar local or has traces set then the runtime version
0451: // of the lappend command will be used.
0452:
0453: protected final TclObject lappendVarScalar(final Interp interp,
0454: final String varname, final TclObject[] values,
0455: final Var[] compiledLocals, final int localIndex)
0456: throws TclException {
0457: Var var = compiledLocals[localIndex];
0458:
0459: // Use runtime impl of lappend if resolved var
0460: // is not available. The lappend command
0461: // accepts an undefined variable name, but we
0462: // don't optimize that case.
0463:
0464: if (var == null || ((var = Var.resolveScalar(var)) == null)) {
0465: return TJC.lappendVar(interp, varname, null, values);
0466: }
0467:
0468: // The cache var is valid, but it might indicate
0469: // a shared value. If the value is shared then
0470: // we need to duplicate it and invoke setVar()
0471: // to implement "copy on write".
0472:
0473: TclObject varValue = var.tobj;
0474: boolean createdNewObj = false;
0475:
0476: if (varValue.isShared()) {
0477: varValue = varValue.duplicate();
0478: createdNewObj = true;
0479: }
0480:
0481: // Insert the new elements at the end of the list.
0482:
0483: final int len = values.length;
0484: if (len == 1) {
0485: TclList.append(interp, varValue, values[0]);
0486: } else {
0487: TclList.append(interp, varValue, values, 0, len);
0488: }
0489:
0490: if (createdNewObj) {
0491: TJC.setVarScalar(var, varValue);
0492: }
0493: return varValue;
0494: }
0495:
0496: // lappendVarArray() will append list elements to
0497: // an array element in a compiled local array variable.
0498: // If the variable is not an array variable then the runtime
0499: // implementation of the lappend command will be used.
0500:
0501: protected final TclObject lappendVarArray(final Interp interp,
0502: final String varname, final String key,
0503: final TclObject[] values, final Var[] compiledLocals,
0504: final int localIndex) throws TclException {
0505: Var var = compiledLocals[localIndex];
0506:
0507: // Use runtime impl of lappend if resolved array
0508: // var is null or is not valid. The lappend command
0509: // accepts an undefined variable name, but we
0510: // don't optimize that case.
0511:
0512: if (var == null || ((var = Var.resolveArray(var)) == null)) {
0513: return TJC.lappendVar(interp, varname, key, values);
0514: }
0515:
0516: // The cache var is valid but need to lookup the
0517: // array element to see if it exists. If the
0518: // array element does not exist then it is
0519: // assumed to be an empty list. If the element
0520: // does exist, then check to see if it is
0521: // shared and if so then make a copy to
0522: // implement "copy on write".
0523:
0524: TclObject varValue = Var.getVarCompiledLocalArray(interp,
0525: varname, key, var, false);
0526:
0527: if (varValue == null) {
0528: // Array element does not exist, use {}
0529: varValue = TclList.newInstance();
0530: } else if (varValue.isShared()) {
0531: varValue = varValue.duplicate();
0532: }
0533:
0534: // Insert the new elements at the end of the list.
0535:
0536: final int len = values.length;
0537: if (len == 1) {
0538: TclList.append(interp, varValue, values[0]);
0539: } else {
0540: TclList.append(interp, varValue, values, 0, len);
0541: }
0542:
0543: return Var.setVarCompiledLocalArray(interp, varname, key,
0544: varValue, var);
0545: }
0546:
0547: // appendVarScalar() will append string elements to
0548: // a scalar local variable. If the variable is not a
0549: // scalar local or has traces set then the runtime version
0550: // of the append command will be used.
0551:
0552: protected final TclObject appendVarScalar(final Interp interp,
0553: final String varname, final TclObject[] values,
0554: final Var[] compiledLocals, final int localIndex)
0555: throws TclException {
0556: Var var = compiledLocals[localIndex];
0557:
0558: // Use runtime impl of append if resolved var
0559: // is not available. The append command
0560: // accepts an undefined variable name, but we
0561: // don't optimize that case.
0562:
0563: if (var == null || (var = Var.resolveScalar(var)) == null) {
0564: return TJC.appendVar(interp, varname, null, values);
0565: }
0566:
0567: // The cache var is valid, but it might indicate
0568: // a shared value. If the value is shared then
0569: // we need to create a new TclString object
0570: // and drop refs to the previous TclObject value.
0571:
0572: TclObject varValue = var.tobj;
0573: boolean createdNewObj = false;
0574:
0575: if (varValue.isShared()) {
0576: varValue = TclString.newInstance(varValue.toString());
0577: createdNewObj = true;
0578: }
0579:
0580: // Insert the new elements at the end of the string.
0581:
0582: final int len = values.length;
0583: if (len == 1) {
0584: TclString.append(varValue, values[0].toString());
0585: } else {
0586: TclString.append(varValue, values, 0, len);
0587: }
0588:
0589: if (createdNewObj) {
0590: TJC.setVarScalar(var, varValue);
0591: }
0592: return varValue;
0593: }
0594:
0595: // appendVarArray() will append string elements to an
0596: // element inside an array variable.
0597:
0598: protected final TclObject appendVarArray(final Interp interp,
0599: final String varname, final String key,
0600: final TclObject[] values, final Var[] compiledLocals,
0601: final int localIndex) throws TclException {
0602: // This is way lame, but the append command
0603: // semantics for arrays with traces are
0604: // such that we can't use an optimized
0605: // implementation. Use the runtime append
0606: // command implementation for now until
0607: // the Tcl core can be fixed to correct this.
0608: // The implementation should work the same
0609: // way as the lappend command.
0610:
0611: return TJC.appendVar(interp, varname, key, values);
0612: }
0613:
0614: } // end class CompiledCommand
0615:
0616: // Used to create a TJC compiled command. This method will
0617: // use the fully qualified command name passed in to
0618: // create the command in the correct namespace.
0619:
0620: public static void createCommand(Interp interp, // Interp to create command in
0621: String cmdName, // Fully qualified or short name of command
0622: TJC.CompiledCommand cmd) // Instance for new compiled command
0623: throws TclException {
0624: ProcCmd.FindCommandNamespaceResult result = ProcCmd
0625: .FindCommandNamespace(interp, cmdName);
0626:
0627: interp.createCommand(result.cmdFullName, cmd);
0628:
0629: cmd.wcmd = Namespace.findCommand(interp, result.cmdName,
0630: result.ns, TCL.NAMESPACE_ONLY);
0631: }
0632:
0633: // Used to load an init file from the package JAR file.
0634: // This command should be invoked from a TJCExtension
0635: // to source the init Tcl script in the JAR. It is
0636: // possible that the TJC package is not yet loaded
0637: // if java::load was used to load the Extension. Just
0638: // require TJC package to take care of this case.
0639:
0640: public static void sourceInitFile(Interp interp, String init_file,
0641: String[] files, String prefix) throws TclException {
0642: // Install temp source command
0643: interp.eval("package require TJC");
0644: interp.eval("rename ::source ::TJC::source");
0645: interp.createCommand("::source", new InitSourceCmd(init_file,
0646: files, prefix));
0647:
0648: //System.out.println("sourceInitFile: will source \"" + (prefix + init_file) + "\"");
0649: TclException tex = null;
0650: try {
0651: interp.evalResource(prefix + init_file);
0652: } catch (TclException ex) {
0653: tex = ex;
0654: } finally {
0655: // Remove temp source command
0656: interp.eval("rename ::source {}");
0657: interp.eval("rename ::TJC::source ::source");
0658:
0659: if (tex != null) {
0660: interp.setResult(tex.getMessage());
0661: throw tex;
0662: }
0663: }
0664: }
0665:
0666: // This class implements a fake "source" command that is
0667: // only active while a package init file is being sourced
0668: // inside sourceInitFile().
0669:
0670: static class InitSourceCmd implements Command {
0671: String init_file;
0672: String[] files;
0673: String prefix;
0674: HashMap filesTable;
0675:
0676: InitSourceCmd(String init_file, String[] files, String prefix) {
0677: this .init_file = init_file;
0678: this .files = files;
0679: this .prefix = prefix;
0680:
0681: filesTable = new HashMap();
0682: for (int i = 0; i < files.length; i++) {
0683: filesTable.put(files[i], "");
0684: }
0685: }
0686:
0687: public void cmdProc(Interp interp, TclObject[] objv)
0688: throws TclException {
0689: boolean handled = false;
0690:
0691: // Expected syntax "source filename"
0692:
0693: if (objv.length == 2) {
0694: String filepath = objv[1].toString();
0695:
0696: interp.eval("file tail {" + filepath + "}");
0697:
0698: String filename = interp.getResult().toString();
0699:
0700: if (filesTable.containsKey(filename)) {
0701: // Sourced a file in files array, this
0702: // file should be read from a resource.
0703: handled = true;
0704:
0705: //System.out.println("sourceInitFile.files: will source \"" + (prefix + filename) + "\"");
0706: interp.evalResource(prefix + filename);
0707: }
0708: }
0709:
0710: if (!handled) {
0711: // Invoke TJC::source, this is the original source command.
0712: // The original command will deal with error reporting
0713: // as well as invocations that source a file not in "files".
0714: Command cmd = interp.getCommand("TJC::source");
0715: cmd.cmdProc(interp, objv);
0716: }
0717: }
0718: }
0719:
0720: // Resolve a command name into a WrappedCommand reference
0721: // that is cached. Note that this method is only ever
0722: // invoked after a command has been successfully invoked,
0723: // so there should be no need to worry about loading the
0724: // command or dealing with stub commands. If the command
0725: // can't be located, null is returned. This command is
0726: // always invoked after the CallFrame for a compiled
0727: // command has been pushed, so it is safe to assume
0728: // that the current namespace is the namespace the
0729: // command is defined in.
0730:
0731: public static WrappedCommand resolveCmd(Interp interp,
0732: String cmdName) throws TclException {
0733: return Namespace.findCommand(interp, cmdName, null, 0);
0734: }
0735:
0736: // This method is used to set the TclObject value
0737: // contained inside a cached scalar Var reference.
0738: // This method works like interp.setVar(), it should
0739: // be used when a valid cached var reference is
0740: // held.
0741:
0742: static final void setVarScalar(final Var var,
0743: final TclObject newValue) // New value to set varible to, can't be null
0744: {
0745: TclObject oldValue = var.tobj;
0746: if (oldValue != newValue) {
0747: var.tobj = newValue;
0748: newValue.preserve();
0749: if (oldValue != null) {
0750: oldValue.release();
0751: }
0752: }
0753: // Unlike Var.setVar(), this method does not invoke
0754: // setVarScalar() or clearVarUndefined() since a
0755: // cached array variable will never be passed to
0756: // this method and an undefined variable will never
0757: // be cached. This method does not return a value
0758: // since the return value would always be the passed
0759: // in newValue.
0760: return;
0761: }
0762:
0763: // This method is invoked when a compiled incr command
0764: // is inlined. This methods works for both scalar
0765: // variables and array scalars. This method is not
0766: // used for cached variables.
0767:
0768: public static final TclObject incrVar(Interp interp, String part1,
0769: String part2, int incrAmount) throws TclException {
0770: return Var.incrVar(interp, part1, part2, incrAmount,
0771: TCL.LEAVE_ERR_MSG);
0772: }
0773:
0774: // Get a TclObject[] of the given size. This array will
0775: // be allocated quickly from a cache if it is a common size.
0776: // The array must be released by releaseObjv(). Each element
0777: // in the returned array will be null.
0778:
0779: private static final boolean USE_OBJV_CACHE = true;
0780:
0781: public static TclObject[] grabObjv(final Interp interp,
0782: final int size) {
0783: if (USE_OBJV_CACHE) {
0784: return Parser.grabObjv(interp, size);
0785: } else {
0786: return new TclObject[size];
0787: }
0788: }
0789:
0790: // Release the array back into the common array
0791: // cache. This array must have been allocated
0792: // with grabObjv(). This method will not release
0793: // TclObject values in the array, use the
0794: // releaseObjvElems() method for that.
0795:
0796: public static void releaseObjv(final Interp interp,
0797: final TclObject[] objv, final int size) {
0798: if (USE_OBJV_CACHE) {
0799: Parser.releaseObjv(interp, objv, size);
0800: }
0801: }
0802:
0803: // For each non-null TclObject element in the array,
0804: // invoke TclObject.release() and then return the
0805: // array to the common cache of array values.
0806: // The array must have been allocated with grabObjv().
0807:
0808: public static void releaseObjvElems(final Interp interp,
0809: final TclObject[] objv, final int size) {
0810: for (int i = 0; i < size; i++) {
0811: TclObject tobj = objv[i];
0812: if (tobj != null) {
0813: tobj.release();
0814: }
0815: }
0816: if (USE_OBJV_CACHE) {
0817: Parser.releaseObjv(interp, objv, size);
0818: }
0819: }
0820:
0821: // Invoke a Command with TclObject arguments. This method
0822: // will invoke an already resolved Command passed in "cmd" or
0823: // it will resolve objv[0] into a command. This method
0824: // assumes that the ref count of each element in the
0825: // objv was already incremented by the caller. This method
0826: // does not modify or cleanup the passed in objv array.
0827:
0828: public static void invoke(Interp interp, // Interp to invoke command in
0829: Command cmd, // Resolved command ref, null to lookup objv[0]
0830: TclObject[] objv, // TclObject arguments to command
0831: int flags) // Either 0 or TCL.EVAL_GLOBAL. If 0 is passed
0832: // then objv[0] is resolved in the current
0833: // namespace and then the global one. If
0834: // TCL.EVAL_GLOBAL is passed then the command
0835: // is resolved and evaluated in the global namespace.
0836: // If the cmd argument is non-null, then this
0837: // flag only controls evaluation scope.
0838: throws TclException {
0839: boolean grabbed_objv = false;
0840:
0841: // Save copy of interp.varFrame in case TCL.EVAL_GLOBAL is set.
0842: CallFrame savedVarFrame = interp.varFrame;
0843:
0844: // If cmd is null, then resolve objv[0] into a Command.
0845:
0846: if (cmd == null) {
0847: WrappedCommand wcmd;
0848: int fflags = 0;
0849: if ((flags & TCL.EVAL_GLOBAL) != 0) {
0850: fflags |= TCL.GLOBAL_ONLY;
0851: }
0852: // Find the procedure to execute this command. If there isn't one,
0853: // then see if there is a command "unknown". If so, create a new
0854: // word array with "unknown" as the first word and the original
0855: // command words as arguments.
0856: String cmdName = objv[0].toString();
0857: wcmd = Namespace.findCommand(interp, cmdName, null, fflags);
0858: if (wcmd != null) {
0859: cmd = wcmd.cmd;
0860: }
0861: if (cmd == null) {
0862: wcmd = Namespace.findCommand(interp, "unknown", null,
0863: TCL.GLOBAL_ONLY);
0864: if (wcmd != null) {
0865: cmd = wcmd.cmd;
0866: }
0867: if (cmd == null) {
0868: throw new TclException(interp,
0869: "invalid command name \"" + cmdName + "\"");
0870: }
0871: int len = objv.length;
0872: if (len == 0) {
0873: throw new TclRuntimeError("zero length objv array");
0874: }
0875: TclObject[] newObjv = TJC.grabObjv(interp, len + 1);
0876: newObjv[0] = TclString.newInstance("unknown");
0877: newObjv[0].preserve();
0878: for (int i = (len - 1); i >= 0; i--) {
0879: newObjv[i + 1] = objv[i];
0880: }
0881: objv = newObjv;
0882: grabbed_objv = true;
0883: }
0884: }
0885:
0886: try {
0887: interp.preserve();
0888: interp.allowExceptions();
0889:
0890: interp.ready();
0891:
0892: interp.nestLevel++;
0893: interp.cmdCount++;
0894:
0895: // Invoke cmdProc for the command.
0896:
0897: if ((flags & TCL.EVAL_GLOBAL) != 0) {
0898: interp.varFrame = null;
0899: }
0900:
0901: cmd.cmdProc(interp, objv);
0902: } catch (TclException ex) {
0903: // Generate error info that includes the arguments
0904: // to the command and add these to the errorInfo var.
0905:
0906: if (ex.getCompletionCode() == TCL.ERROR
0907: && !(interp.errAlreadyLogged)) {
0908: StringBuffer cmd_strbuf = new StringBuffer(64);
0909:
0910: int len = objv.length;
0911: if (len == 0) {
0912: throw new TclRuntimeError("zero length objv array");
0913: }
0914: for (int i = 0; i < len; i++) {
0915: Util.appendElement(interp, cmd_strbuf, objv[i]
0916: .toString());
0917: }
0918: String cmd_str = cmd_strbuf.toString();
0919: char[] script_array = cmd_str.toCharArray();
0920: int script_index = 0;
0921: int command_start = 0;
0922: int command_length = cmd_str.length();
0923: Parser
0924: .logCommandInfo(interp, script_array,
0925: script_index, command_start,
0926: command_length, ex);
0927: }
0928:
0929: throw ex;
0930: } finally {
0931: if (grabbed_objv) {
0932: objv[0].release();
0933: TJC.releaseObjv(interp, objv, objv.length);
0934: }
0935: interp.nestLevel--;
0936: interp.varFrame = savedVarFrame;
0937: interp.release();
0938:
0939: interp.checkInterrupted();
0940: }
0941: }
0942:
0943: // Most efficient way to query a TclObject
0944: // to determine its boolean value. If a
0945: // TclString is passed into this method,
0946: // it will be parsed and the object's internal
0947: // rep will be updated to a numeric type.
0948:
0949: public static boolean getBoolean(final Interp interp,
0950: final TclObject obj) throws TclException {
0951: if (obj.isIntType()) {
0952: return (obj.ivalue != 0); // Inline TclInteger.get()
0953: } else if (obj.isDoubleType()) {
0954: return (TJC.exprGetKnownDouble(obj) != 0.0);
0955: }
0956:
0957: ExprValue value;
0958: if (USE_EXPR_CACHE) {
0959: value = interp.expr.grabExprValue();
0960: } else {
0961: value = new ExprValue(0, null);
0962: }
0963: // The logic above already checked for an int or
0964: // double type so just reparse from the string
0965: // instead of calling ExprParseObject().
0966: Expression.ExprParseString(interp, obj, value);
0967: boolean b = value.getBooleanValue(interp);
0968: if (USE_EXPR_CACHE) {
0969: interp.expr.releaseExprValue(value);
0970: }
0971: return b;
0972: }
0973:
0974: // This method will invoke logic for the switch
0975: // command at runtime. If no body is matched,
0976: // then -1 will be returned. Otherwise, the
0977: // offset from the first pattern is returned.
0978: // The caller must take care to release the
0979: // pbObjv array after invoking this method.
0980:
0981: public static int invokeSwitch(Interp interp, TclObject[] pbObjv,
0982: int pbStart, // Index > 0 of first pattern
0983: String string, // String to be matched against patterns
0984: int mode) // Either TJC.SWITCH_MODE_EXACT
0985: // or TJC.SWITCH_MODE_GLOB
0986: // or TJC.SWITCH_MODE_REGEXP
0987: throws TclException {
0988: int offset = SwitchCmd.getBodyOffset(interp, pbObjv, pbStart,
0989: string, mode);
0990: return offset;
0991: }
0992:
0993: // Check a switch string and raise an error
0994: // if it starts with a '-' character. This
0995: // runtime check is needed for a compiled
0996: // switch command like [switch $str {...}]
0997: // which has no option terminator "--".
0998:
0999: public static void switchStringIsNotOption(Interp interp, String str)
1000: throws TclException {
1001: if (str.startsWith("-")) {
1002: TclObject[] objv = new TclObject[1];
1003: objv[0] = TclString.newInstance("switch");
1004: throw new TclNumArgsException(interp, 1, objv,
1005: "?switches? string pattern body ... ?default body?");
1006: }
1007: }
1008:
1009: // Raise exception when a variable could not
1010: // be set during a catch command. This
1011: // exception is defined here so that the
1012: // constant string need not appear in
1013: // every class file.
1014:
1015: public static void catchVarErr(Interp interp) throws TclException {
1016: throw new TclException(interp,
1017: "couldn't save command result in variable");
1018: }
1019:
1020: // Raise exception when a loop variable could
1021: // not be set in a foreach command.
1022:
1023: public static void foreachVarErr(Interp interp, String varname)
1024: throws TclException {
1025: throw new TclException(interp, "couldn't set loop variable: \""
1026: + varname + "\"");
1027: }
1028:
1029: private static final boolean USE_EXPR_CACHE = true;
1030:
1031: // Release an ExprValue that was returned by
1032: // one of the exprGetValue methods.
1033:
1034: public static void exprReleaseValue(Interp interp, ExprValue value) {
1035: if (USE_EXPR_CACHE) {
1036: interp.expr.releaseExprValue(value);
1037: }
1038: }
1039:
1040: // Return the ExprValue for the given int value.
1041: // If the value was parsed from a String that
1042: // differs from the parsed value of the integer,
1043: // then it should be passed as the srep.
1044:
1045: public static ExprValue exprGetValue(Interp interp, int ival,
1046: String srep) throws TclException {
1047: if (USE_EXPR_CACHE) {
1048: ExprValue value = interp.expr.grabExprValue();
1049: value.setIntValue(ival, srep);
1050: return value;
1051: } else {
1052: return new ExprValue(ival, srep);
1053: }
1054: }
1055:
1056: // Return the expr value contained in the double
1057:
1058: public static ExprValue exprGetValue(Interp interp, double dval,
1059: String srep) throws TclException {
1060: if (USE_EXPR_CACHE) {
1061: ExprValue value = interp.expr.grabExprValue();
1062: value.setDoubleValue(dval, srep);
1063: return value;
1064: } else {
1065: return new ExprValue(dval, srep);
1066: }
1067: }
1068:
1069: // Return the expr value for the String
1070:
1071: public static ExprValue exprGetValue(Interp interp, String srep)
1072: throws TclException {
1073: if (USE_EXPR_CACHE) {
1074: ExprValue value = interp.expr.grabExprValue();
1075: value.setStringValue(srep);
1076: return value;
1077: } else {
1078: return new ExprValue(srep);
1079: }
1080: }
1081:
1082: // Return the expr value for the boolean, this
1083: // boolean has no string rep and is represented
1084: // by an integer type.
1085:
1086: public static ExprValue exprGetValue(Interp interp, boolean bval)
1087: throws TclException {
1088: if (USE_EXPR_CACHE) {
1089: ExprValue value = interp.expr.grabExprValue();
1090: value.setIntValue(bval);
1091: return value;
1092: } else {
1093: return new ExprValue(bval);
1094: }
1095: }
1096:
1097: // Return the expr value contained in the TclObject
1098:
1099: public static ExprValue exprGetValue(Interp interp, TclObject tobj)
1100: throws TclException {
1101: if (USE_EXPR_CACHE) {
1102: ExprValue value = interp.expr.grabExprValue();
1103: Expression.ExprParseObject(interp, tobj, value);
1104: return value;
1105: } else {
1106: ExprValue value = new ExprValue(0, null);
1107: Expression.ExprParseObject(interp, tobj, value);
1108: return value;
1109: }
1110: }
1111:
1112: // Init expr value with the value in the TclObject
1113:
1114: public static void exprInitValue(final Interp interp,
1115: final ExprValue value, final TclObject tobj)
1116: throws TclException {
1117: Expression.ExprParseObject(interp, tobj, value);
1118: }
1119:
1120: // Return an uninitialized ExprValue.
1121:
1122: public static ExprValue exprGetValue(Interp interp) {
1123: if (USE_EXPR_CACHE) {
1124: return interp.expr.grabExprValue();
1125: } else {
1126: return new ExprValue(0, null);
1127: }
1128: }
1129:
1130: // Return the int value inside a TclObject known
1131: // to be of type integer. This method should
1132: // only be used inside the expr layer. Testing
1133: // indicates that this method executes 25%
1134: // faster than TclInteger.get().
1135:
1136: public static int exprGetKnownInt(final TclObject tobj) {
1137: return tobj.ivalue;
1138: }
1139:
1140: // Return the double value inside a TclObject known
1141: // to be of type double. This method should
1142: // only be used inside the expr layer. Testing
1143: // indicates that this method executes about
1144: // 4x faster than TclDouble.get().
1145:
1146: public static double exprGetKnownDouble(final TclObject tobj) {
1147: return ((TclDouble) tobj.getInternalRep()).value;
1148: }
1149:
1150: // Evaluate a unary expr operator.
1151:
1152: public static void exprUnaryOperator(final Interp interp, // current interp, can't be null.
1153: final int op, // One of the EXPR_OP_* values
1154: final ExprValue value) throws TclException {
1155: Expression.evalUnaryOperator(interp, op, value);
1156: }
1157:
1158: // Evaluate a binary expr operator.
1159:
1160: public static void exprBinaryOperator(final Interp interp, // current interp, can't be null.
1161: final int op, // One of the EXPR_OP_* values
1162: final ExprValue value, final ExprValue value2)
1163: throws TclException {
1164: Expression.evalBinaryOperator(interp, op, value, value2);
1165: }
1166:
1167: // Evaluate a math function. This method will release
1168: // the values ExprValue objects when finished. The
1169: // values argument should be null where the math
1170: // function takes no arguments.
1171:
1172: public static void exprMathFunction(Interp interp, // current interp, can't be null.
1173: String funcName, // Name of math function
1174: ExprValue[] values, // Array of arguments, can be null
1175: ExprValue result) // Location to store result
1176: throws TclException {
1177: interp.expr.evalMathFunction(interp, funcName, values, false,
1178: result);
1179: }
1180:
1181: // Set the interp result to the given expr value. This
1182: // method is used only for a compiled version of the expr
1183: // command.
1184:
1185: public static void exprSetResult(Interp interp, ExprValue value)
1186: throws TclException {
1187: switch (value.getType()) {
1188: case ExprValue.INT:
1189: interp.setResult(value.getIntValue());
1190: break;
1191: case ExprValue.DOUBLE:
1192: interp.setResult(value.getDoubleValue());
1193: break;
1194: case ExprValue.STRING:
1195: interp.setResult(value.getStringValue());
1196: break;
1197: default:
1198: throw new TclRuntimeError(
1199: "internal error: expression, unknown");
1200: }
1201: return;
1202: }
1203:
1204: // Determine if the given TclObject is equal to
1205: // the empty string. This method implements an
1206: // optimized version of an expr comparison
1207: // like expr {$obj == ""} or expr {$obj != {}}.
1208: // This method sets the value argument to
1209: // an integer, either 0 or 1.
1210:
1211: public static void exprEqualsEmptyString(ExprValue value, // Location for result value
1212: TclObject obj, // TclObject to compare to empty string
1213: final boolean negate) // Negate result if true
1214: throws TclException {
1215: boolean isEmptyString;
1216:
1217: if (obj.hasNoStringRep() && obj.isListType()) {
1218: // A pure Tcl list is equal to the empty string
1219: // when the list length is zero. This check
1220: // avoids the possibly slow generation of
1221: // a string rep from a pure TclList object.
1222: // Note that passing null as the Interp
1223: // argument is fine since we know this object
1224: // has the TclList internal rep already.
1225: isEmptyString = (TclList.getLength(null, obj) == 0);
1226: } else {
1227: // TclObject already has a string rep, check if it is a
1228: // ref to the interned empty string or if the len is 0.
1229: String s = obj.toString();
1230: isEmptyString = (s == "" || s.length() == 0);
1231: }
1232: if (negate) {
1233: isEmptyString = !isEmptyString;
1234: }
1235: value.setIntValue(isEmptyString);
1236: }
1237:
1238: // Implement an optimized version of the
1239: // int() math function used in an expr.
1240: // This method will raise an error if
1241: // the value is non-numeric, otherwise
1242: // it will case a double type to an int.
1243:
1244: public static void exprIntMathFunction(Interp interp,
1245: ExprValue value) throws TclException {
1246: switch (value.getType()) {
1247: case ExprValue.INT: {
1248: // No-op
1249: break;
1250: }
1251: case ExprValue.DOUBLE: {
1252: double d = value.getDoubleValue();
1253: if (((d < 0) && (d < ((double) TCL.INT_MIN)))
1254: || ((d > 0) && (d > ((double) TCL.INT_MAX)))) {
1255: Expression.IntegerTooLarge(interp);
1256: }
1257: value.setIntValue((int) d);
1258: break;
1259: }
1260: case ExprValue.STRING: {
1261: throw new TclException(interp,
1262: "argument to math function didn't have numeric value");
1263: }
1264: }
1265: }
1266:
1267: // Implement an optimized version of the
1268: // double() math function used in an expr.
1269: // This method will raise an error if
1270: // the value is non-numeric, otherwise
1271: // it will case an int type to a double.
1272:
1273: public static void exprDoubleMathFunction(Interp interp,
1274: ExprValue value) throws TclException {
1275: if (value.isStringType()) {
1276: throw new TclException(interp,
1277: "argument to math function didn't have numeric value");
1278: } else if (value.isIntType()) {
1279: value.setDoubleValue((double) value.getIntValue());
1280: }
1281: }
1282:
1283: // Evaluate a unary not operator. This method accepts an
1284: // ExprValue value and returns the result in the ExprValue.
1285: // This method is invoked when an ExprValue is known
1286: // at compile time to be a double or a string.
1287: // This method should not be invoked when the ExprValue
1288: // is known to be of type int (or boolean).
1289:
1290: public static void exprUnaryNotOperator(final Interp interp, // current interp, can't be null.
1291: final ExprValue value) // operand value
1292: throws TclException {
1293: // Special case of int type should have been handled
1294: // in inline code before this method is invoked.
1295:
1296: if (value.isDoubleType()) {
1297: value.setIntValue(value.getDoubleValue() == 0.0);
1298: } else {
1299: Expression.evalUnaryOperator(interp, TJC.EXPR_OP_UNARY_NOT,
1300: value);
1301: }
1302: }
1303:
1304: // Evaluate a unary not operator. This method accepts a
1305: // TclObject value and returns the result in an ExprValue.
1306:
1307: public static void exprUnaryNotOperator(final Interp interp, // current interp, can't be null.
1308: final ExprValue value, // where to store result of operator
1309: final TclObject tobj) // contains the value as a TclObject
1310: throws TclException {
1311: // Special case of int type should have been handled
1312: // in inline code before this method is invoked.
1313:
1314: if (tobj.isDoubleType()) {
1315: value.setIntValue(TJC.exprGetKnownDouble(tobj) == 0.0);
1316: return;
1317: }
1318:
1319: Expression.ExprParseString(interp, tobj, value);
1320: if (value.isIntType()) {
1321: // ExprValue is known to be an int, so
1322: // invoke specific method that does not
1323: // change the type.
1324:
1325: value.optIntUnaryNot();
1326: } else if (value.isDoubleType()) {
1327: value.setIntValue(value.getDoubleValue() == 0.0);
1328: } else {
1329: Expression.evalUnaryOperator(interp, EXPR_OP_UNARY_NOT,
1330: value);
1331: }
1332: }
1333:
1334: // These methods are used when inlining a
1335: // unary not operator. In the case where
1336: // the operand is known to be of type int,
1337: // these methods are used to inline the op.
1338: // This impl executes about 4x faster than
1339: // a exprUnaryNotOperator() that checks
1340: // for a TclObject that contains an int.
1341:
1342: public static void exprUnaryNotOperatorKnownInt(
1343: final ExprValue value, // where to store result of operator
1344: final TclObject tobj) // contains the value as a TclObject
1345: {
1346: // Invoking ExprValue.setInt(boolean) here is the most
1347: // efficient implementation. Invoking TJC.exprGetKnownInt()
1348: // and inlining the branching logic in calling code is slower.
1349:
1350: value.setIntValue(tobj.ivalue == 0);
1351: }
1352:
1353: public static int exprUnaryNotOperatorKnownInt(final TclObject tobj) // contains the value as a TclObject
1354: {
1355: return (tobj.ivalue == 0) ? 1 : 0;
1356: }
1357:
1358: public static boolean exprUnaryNotOperatorKnownIntAsBoolean(
1359: final TclObject tobj) // contains the value as a TclObject
1360: {
1361: return (tobj.ivalue == 0);
1362: }
1363:
1364: // Implements inlined global command, this method
1365: // will create a local var linked to a global var.
1366: // A compiled global command accepts a varTail
1367: // that is either a scalar or an array name.
1368: // If the localIndex argument is not -1, it
1369: // indicates the compiledLocal slot to use.
1370:
1371: public static final void makeGlobalLinkVar(Interp interp,
1372: String varName, // Fully qualified name of global variable.
1373: String varTail, // Variable name without namespace qualifiers.
1374: int localIndex) // Index into compiledLocals array
1375: throws TclException {
1376: // Link to the variable "varName" in the global :: namespace.
1377: // A local link var named varTail is defined.
1378:
1379: Var.makeUpvar(interp, null, varName, null, TCL.GLOBAL_ONLY,
1380: varTail, 0, localIndex);
1381: }
1382:
1383: // Implements inlined lindex command for non-constant integer
1384: // index values. This implementation is used only with lindex
1385: // commands that have 3 arguments. If the index argument
1386: // is already a TclInteger type then an optimized lindex
1387: // impl is used. The interp result is always set by this
1388: // method.
1389:
1390: public static final void lindexNonconst(final Interp interp,
1391: final TclObject listObj, // List value
1392: final TclObject indexValue) // List index to be resolved.
1393: throws TclException {
1394: // Optimized check for integer indexValue argument.
1395: // This is the most common use case:
1396: // set obj [lindex $list $i]
1397:
1398: if (indexValue.isIntType()) {
1399: TclObject result = TclList.index(interp, listObj,
1400: indexValue.ivalue);
1401: if (result == null) {
1402: interp.resetResult();
1403: } else {
1404: interp.setResult(result);
1405: }
1406: return;
1407: } else {
1408: // Invoke the static lindex command impl.
1409:
1410: TclObject[] objv = TJC.grabObjv(interp, 3);
1411: try {
1412: //objv[0] = null;
1413:
1414: objv[1] = listObj;
1415:
1416: objv[2] = indexValue;
1417: indexValue.preserve();
1418:
1419: TclObject elem = LindexCmd.TclLindexList(interp,
1420: listObj, objv, 2);
1421: interp.setResult(elem);
1422: elem.release();
1423: } finally {
1424: // Caller should preserve() and release() listObj
1425: objv[2].release();
1426: TJC.releaseObjv(interp, objv, 3);
1427: }
1428: return;
1429: }
1430: }
1431:
1432: // Implements inlined lappend command that appends 1 or more
1433: // TclObject values to a variable. This implementation
1434: // makes use of runtime support found in the LappendCmd class.
1435: // The new variable value after the lappend operation is returned.
1436:
1437: public static final TclObject lappendVar(Interp interp,
1438: String varName, // Name of variable
1439: String key, // Array element key (can be null)
1440: TclObject[] values) // Array of TclObject values to append
1441: throws TclException {
1442: if (key == null) {
1443: return LappendCmd.lappendVar(interp, varName, values, 0);
1444: } else {
1445: // LappendCmd expects a single var name in a String,
1446: // so create one for this uncommon case.
1447: String avName = varName + "(" + key + ")";
1448: return LappendCmd.lappendVar(interp, avName, values, 0);
1449: }
1450: }
1451:
1452: // Implements inlined append command that appends 1 or more
1453: // TclObject values to a variable. This implementation
1454: // duplicates the logic found in AppendCmd. The new
1455: // variable value after the lappend operation is returned.
1456:
1457: public static final TclObject appendVar(Interp interp,
1458: String varName, // Name of variable
1459: String key, // Array element key (can be null)
1460: TclObject[] values) // Array of TclObject values to append
1461: throws TclException {
1462: TclObject varValue = null;
1463: final int len = values.length;
1464: if (key != null) {
1465: varName = varName + "(" + key + ")";
1466: }
1467:
1468: for (int i = 0; i < len; i++) {
1469: varValue = interp.setVar(varName, values[i],
1470: TCL.APPEND_VALUE);
1471: }
1472:
1473: if (varValue == null) {
1474: // Return empty result object if null
1475: varValue = interp.checkCommonString(null);
1476: }
1477:
1478: return varValue;
1479: }
1480:
1481: // Implements inlined string index command. This
1482: // implementation duplicates the logic found
1483: // in StringCmd.java. The new value is returned.
1484: // If the index is out of range the null result
1485: // will be returned.
1486:
1487: public static final TclObject stringIndex(final Interp interp,
1488: final String str, // string
1489: final TclObject indObj) // index into string
1490: throws TclException {
1491: final int len = str.length();
1492: int i;
1493:
1494: if (indObj.isIntType()) {
1495: i = indObj.ivalue; // Inline TclInteger.get()
1496: } else {
1497: i = Util.getIntForIndex(interp, indObj, len - 1);
1498: }
1499:
1500: if ((i >= 0) && (i < len)) {
1501: TclObject obj = interp.checkCommonCharacter(str.charAt(i));
1502: if (obj == null) {
1503: obj = TclString.newInstance(str.substring(i, i + 1));
1504: }
1505: return obj;
1506: } else {
1507: return interp.checkCommonString(null);
1508: }
1509: }
1510:
1511: // Implements inlined string range command. This
1512: // implementation duplicates the logic found
1513: // in StringCmd.java. The new value is returned.
1514: // This method assumes that the firstObj TclObject
1515: // was preserved() before this method is invoked.
1516: // This method will always release() the firstObj.
1517:
1518: public static final TclObject stringRange(final Interp interp,
1519: final String str, // string
1520: final TclObject firstObj, // first index (ref count incremented)
1521: final TclObject lastObj) // last index
1522: throws TclException {
1523: final int len = str.length();
1524: int first, last;
1525:
1526: try {
1527: if (firstObj.isIntType()) {
1528: first = firstObj.ivalue; // Inline TclInteger.get()
1529: } else {
1530: first = Util.getIntForIndex(interp, firstObj, len - 1);
1531: }
1532: if (first < 0) {
1533: first = 0;
1534: }
1535:
1536: if (lastObj.isIntType()) {
1537: last = lastObj.ivalue; // Inline TclInteger.get()
1538: } else {
1539: last = Util.getIntForIndex(interp, lastObj, len - 1);
1540: }
1541: if (last >= len) {
1542: last = len - 1;
1543: }
1544: } finally {
1545: // Release firstObj after lastObj has been queried.
1546: // There could only be a ref count problem if firstObj
1547: // was released before lastObj and lastObj was a list
1548: // element of firstObj and lastObj had a ref count of 1.
1549:
1550: firstObj.release();
1551: }
1552:
1553: if (first > last) {
1554: return interp.checkCommonString(null);
1555: } else {
1556: String substr = str.substring(first, last + 1);
1557: return TclString.newInstance(substr);
1558: }
1559: }
1560:
1561: // Implements inlined "string first" command, this
1562: // duplicates the logic found in StringCmd.java.
1563: // A TclObject that holds the new value is
1564: // returned.
1565:
1566: public static final TclObject stringFirst(Interp interp,
1567: String substr, // substring to search for
1568: String str, // string to search in
1569: TclObject startObj) // start index (null if start is 0)
1570: throws TclException {
1571: int substrLen = substr.length();
1572: int strLen = str.length();
1573: int index;
1574:
1575: int start;
1576:
1577: if (startObj == null) {
1578: start = 0;
1579: } else {
1580: // If a startIndex is specified, we will need to fast
1581: // forward to that point in the string before we think
1582: // about a match.
1583:
1584: start = Util.getIntForIndex(interp, startObj, strLen - 1);
1585: if (start >= strLen) {
1586: return interp.checkCommonInteger(-1);
1587: }
1588: }
1589:
1590: if (substrLen == 0) {
1591: index = -1;
1592: } else if (substrLen == 1) {
1593: char c = substr.charAt(0);
1594: index = str.indexOf(c, start);
1595: } else {
1596: index = str.indexOf(substr, start);
1597: }
1598: return interp.checkCommonInteger(index);
1599: }
1600:
1601: // Implements inlined "string last" command, this
1602: // duplicates the logic found in StringCmd.java.
1603: // A TclObject that holds the new value is
1604: // returned.
1605:
1606: public static final TclObject stringLast(Interp interp,
1607: String substr, // substring to search for
1608: String str, // string to search in
1609: TclObject lastObj) // last index (null if last is 0)
1610: throws TclException {
1611: int substrLen = substr.length();
1612: int strLen = str.length();
1613: int index;
1614: int last;
1615:
1616: if (lastObj == null) {
1617: last = 0;
1618: } else {
1619: last = Util.getIntForIndex(interp, lastObj, strLen - 1);
1620: if (last < 0) {
1621: return interp.checkCommonInteger(-1);
1622: } else if (last < strLen) {
1623: str = str.substring(0, last + 1);
1624: }
1625: }
1626:
1627: if (substrLen == 0) {
1628: index = -1;
1629: } else if (substrLen == 1) {
1630: char c = substr.charAt(0);
1631: index = str.lastIndexOf(c);
1632: } else {
1633: index = str.lastIndexOf(substr);
1634: }
1635: return interp.checkCommonInteger(index);
1636: }
1637:
1638: }
|