Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | More tests, comments and improvements to initial implementation. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | dah-proc-arg-upvar |
Files: | files | file ages | folders |
SHA1: |
ed5acdb9877061a6d50ee9ac4258a809 |
User & Date: | dah 2016-12-06 19:35:26 |
Context
2016-12-16
| ||
11:22 | Partial reimplementation. Retain value passed in by caller, support defaults check-in: 173792e010 user: dah tags: dah-proc-arg-upvar | |
2016-12-06
| ||
19:35 | More tests, comments and improvements to initial implementation. check-in: ed5acdb987 user: dah tags: dah-proc-arg-upvar | |
2016-12-01
| ||
22:07 | [507d9b9651a3c903] Possible implementation of auto-upvar for procedures. check-in: 6e39d87cbc user: dkf tags: dah-proc-arg-upvar | |
Changes
Changes to generic/tclProc.c.
︙ | ︙ | |||
496 497 498 499 500 501 502 | localPtr = procPtr->firstLocalPtr; } else { procPtr->numArgs = numArgs; procPtr->numCompiledLocals = numArgs; } for (i = 0; i < numArgs; i++) { | | | > | | | | 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 | localPtr = procPtr->firstLocalPtr; } else { procPtr->numArgs = numArgs; procPtr->numCompiledLocals = numArgs; } for (i = 0; i < numArgs; i++) { int fieldCount, nameLength, valueLength, varFlags = 0; const char **fieldValues, *varName; /* * Now divide the specifier up into name and default. */ result = Tcl_SplitList(interp, argArray[i], &fieldCount, &fieldValues); if (result != TCL_OK) { goto procError; } varName = fieldValues[0]; if (fieldCount > 2) { ckfree(fieldValues); Tcl_SetObjResult(interp, Tcl_ObjPrintf( "too many fields in argument specifier \"%s\"", argArray[i])); Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", "FORMALARGUMENTFORMAT", NULL); goto procError; } if ((fieldCount == 0) || (*varName == 0)) { ckfree(fieldValues); Tcl_SetObjResult(interp, Tcl_NewStringObj( "argument with no name", -1)); Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", "FORMALARGUMENTFORMAT", NULL); goto procError; } nameLength = strlen(varName); if (fieldCount == 2) { valueLength = strlen(fieldValues[1]); } else { valueLength = 0; } /* * Check that the formal parameter name is a scalar. */ p = varName; while (*p != '\0') { if (*p == '(') { const char *q = p; do { q++; } while (*q != '\0'); q--; |
︙ | ︙ | |||
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | ckfree(fieldValues); Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", "FORMALARGUMENTFORMAT", NULL); goto procError; } p++; } if (precompiled) { /* * Compare the parsed argument with the stored one. Note that the * only flag value that makes sense at this point is VAR_ARGUMENT * (its value was kept the same as pre VarReform to simplify * tbcload's processing of older byetcodes). * | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 | ckfree(fieldValues); Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", "FORMALARGUMENTFORMAT", NULL); goto procError; } p++; } if ((i == numArgs - 1) && (nameLength == 4) && (*varName == 'a') && (strcmp(varName, "args") == 0)) { varFlags |= VAR_IS_ARGS; } else if (*varName == '*' && nameLength > 1) { /* * Names that begin with an asterisk shall be handled as a link * var to be linked at some point in the future. */ if (fieldCount == 2) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "procedure \"%s\": formal parameter \"%s\" " " is to be a link and can't have a default value", procName, fieldValues[0])); ckfree(fieldValues); Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", "FORMALARGUMENTFORMAT", NULL); goto procError; } else { varName++; nameLength--; /* * Indicate this argument is to be a future link var. Does * there need to be a new VAR_FUTURE_LINK flag? */ varFlags |= VAR_LINK; } } if (precompiled) { /* * Compare the parsed argument with the stored one. Note that the * only flag value that makes sense at this point is VAR_ARGUMENT * (its value was kept the same as pre VarReform to simplify * tbcload's processing of older byetcodes). * * The only other flag value that is important to retrieve from * precompiled procs is VAR_TEMPORARY (also unchanged). It is * needed later when retrieving the variable names. */ if ((localPtr->nameLength != nameLength) || (strcmp(localPtr->name, varName)) || (localPtr->frameIndex != i) || !(localPtr->flags & VAR_ARGUMENT) || (localPtr->defValuePtr == NULL && fieldCount == 2) || (localPtr->defValuePtr != NULL && fieldCount != 2)) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "procedure \"%s\": formal parameter %d is " "inconsistent with precompiled body", procName, i)); |
︙ | ︙ | |||
614 615 616 617 618 619 620 | procName, fieldValues[0])); ckfree(fieldValues); Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", "BYTECODELIES", NULL); goto procError; } } | | < < < > | > | > > | < < | | | | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | | < < < < < | < | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 | procName, fieldValues[0])); ckfree(fieldValues); Tcl_SetErrorCode(interp, "TCL", "OPERATION", "PROC", "BYTECODELIES", NULL); goto procError; } } /* * Set the VAR_IS_ARGS flag, etc, if needed. */ if (varFlags) { localPtr->flags |= varFlags; } localPtr = localPtr->nextPtr; } else { /* * Allocate an entry in the runtime procedure frame's array of * local variables for the argument. */ localPtr = ckalloc(TclOffset(CompiledLocal, name) + nameLength+1); if (procPtr->firstLocalPtr == NULL) { procPtr->firstLocalPtr = procPtr->lastLocalPtr = localPtr; } else { procPtr->lastLocalPtr->nextPtr = localPtr; procPtr->lastLocalPtr = localPtr; } localPtr->nextPtr = NULL; localPtr->nameLength = nameLength; localPtr->frameIndex = i; localPtr->flags = (varFlags | VAR_ARGUMENT); localPtr->resolveInfo = NULL; if (fieldCount == 2) { localPtr->defValuePtr = Tcl_NewStringObj(fieldValues[1], valueLength); Tcl_IncrRefCount(localPtr->defValuePtr); } else { localPtr->defValuePtr = NULL; } memcpy(localPtr->name, varName, nameLength + 1); } ckfree(fieldValues); } *procPtrPtr = procPtr; ckfree(argArray); return TCL_OK; |
︙ | ︙ | |||
1418 1419 1420 1421 1422 1423 1424 | * Results: * A standard Tcl result. * * Side effects: * Allocates memory on the stack for the compiled local variables, the * caller is responsible for freeing them. Initialises all variables. May * invoke various name resolvers in order to determine which variables | | > | | 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 | * Results: * A standard Tcl result. * * Side effects: * Allocates memory on the stack for the compiled local variables, the * caller is responsible for freeing them. Initialises all variables. May * invoke various name resolvers in order to determine which variables * are being referenced at runtime. Links variables for the caller when * a formal parameter has the VAR_LINK flag. * *---------------------------------------------------------------------- */ static int InitArgsAndLocals( register Tcl_Interp *interp,/* Interpreter in which procedure was * invoked. */ Tcl_Obj *procNameObj, /* Procedure name for error reporting. */ int skip) /* Number of initial arguments to be skipped, * i.e., words in the "command name". */ { CallFrame *framePtr = ((Interp *)interp)->varFramePtr; register Proc *procPtr = framePtr->procPtr; ByteCode *codePtr = procPtr->bodyPtr->internalRep.twoPtrValue.ptr1; register Var *varPtr, *defPtr; int localCt = procPtr->numCompiledLocals, numArgs, argCt, imax, i = 0; Tcl_Obj *const *argObjs; /* * Make sure that the local cache of variable names and initial values has * been initialised properly . */ |
︙ | ︙ | |||
1483 1484 1485 1486 1487 1488 1489 | if (argCt) { goto incorrectArgs; } else { goto correctArgs; } } | > > > > > | | | | > > > > > > | > | > > | > > > > > > > | | 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 | if (argCt) { goto incorrectArgs; } else { goto correctArgs; } } /* * If the command has any formals with the VAR_LINK flag then * cmdPtr->flags will have CMD_HAS_ARG_LINKS. Walk through the * the proc's local variable list and set things up as needed. */ if (procPtr->cmdPtr->flags & CMD_HAS_ARG_LINKS) { CallFrame *upFramePtr = NULL; Var *otherPtr, *arrayPtr; CompiledLocal *localPtr = procPtr->firstLocalPtr; int done = 1; imax = ((argCt < numArgs) ? argCt : numArgs); for (; i < imax; i++, localPtr = localPtr->nextPtr, varPtr++, defPtr ? defPtr++ : defPtr) { Tcl_Obj *objPtr = argObjs[i]; /* * Now check if this formal was defined to be linked to its * corresponding argument. The formal doesn't do * any linking itself. */ if (TclIsVarLink(localPtr)) { if (upFramePtr == NULL) { if (TclObjGetFrame(interp, NULL, &upFramePtr) == -1) { i = -1; /* Tell incorrectArgs we set the error */ goto incorrectArgs; } } /* * Locate the other variable. */ ((Interp *)interp)->varFramePtr = upFramePtr; otherPtr = TclObjLookupVarEx(interp, objPtr, NULL, TCL_LEAVE_ERR_MSG, "access", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); ((Interp *)interp)->varFramePtr = framePtr; if (otherPtr == NULL) { i = -1; /* Tell incorrectArgs we set the error */ goto incorrectArgs; } varPtr->flags = VAR_LINK; varPtr->value.linkPtr = otherPtr; if (TclIsVarInHash(otherPtr)) { VarHashRefCount(otherPtr)++; } } else if (i != numArgs-1) { varPtr->flags = 0; varPtr->value.objPtr = objPtr; Tcl_IncrRefCount(objPtr); /* Local var is a reference */ } else { /* * The last non-linked formal could be 'args'. Let the 'args' * checking code handle it. */ done = 0; break; } } /* * These tests are true only when all arguments are provided by the * caller and there is no formal 'args'. */ if (done && argCt == numArgs) { goto correctArgs; } } else { imax = ((argCt < numArgs-1) ? argCt : numArgs-1); for (; i < imax; i++, varPtr++, defPtr ? defPtr++ : defPtr) { /* * "Normal" arguments; last formal is special, depends on it being * 'args'. */ Tcl_Obj *objPtr = argObjs[i]; |
︙ | ︙ | |||
1622 1623 1624 1625 1626 1627 1628 | incorrectArgs: if ((skip != 1) && TclInitRewriteEnsemble(interp, skip-1, 0, framePtr->objv)) { TclNRAddCallback(interp, TclClearRootEnsemble, NULL, NULL, NULL, NULL); } memset(varPtr, 0, ((framePtr->compiledLocals + localCt)-varPtr) * sizeof(Var)); | | | 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 | incorrectArgs: if ((skip != 1) && TclInitRewriteEnsemble(interp, skip-1, 0, framePtr->objv)) { TclNRAddCallback(interp, TclClearRootEnsemble, NULL, NULL, NULL, NULL); } memset(varPtr, 0, ((framePtr->compiledLocals + localCt)-varPtr) * sizeof(Var)); return (i != -1 ? ProcWrongNumArgs(interp, skip) : TCL_ERROR); } /* *---------------------------------------------------------------------- * * TclPushProcCallFrame -- * |
︙ | ︙ |
Changes to tests/oo.test.
︙ | ︙ | |||
3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 | } {} test oo-35.4 {Bug 593baa032c: mixins list teardown} { # Bug makes this crash, especially with mem-debugging on oo::class create B {} oo::class create D {mixin B} namespace eval [info object namespace D] [list [namespace which B] destroy] } {} cleanupTests return # Local Variables: # mode: tcl | > > > > > > > > > > > > > > > > > > > > > | 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 | } {} test oo-35.4 {Bug 593baa032c: mixins list teardown} { # Bug makes this crash, especially with mem-debugging on oo::class create B {} oo::class create D {mixin B} namespace eval [info object namespace D] [list [namespace which B] destroy] } {} test oo-36.1 {OO: Auto linking} -setup { oo::class create C } -body { oo::define C { constructor {*a} { incr a lappend ::result $a } method m {*a} { incr a lappend ::result $a } } set a 0 set c [C new a] $c m a return $result } -cleanup { C destroy } -result {1 2} cleanupTests return # Local Variables: # mode: tcl |
︙ | ︙ |
Changes to tests/proc.test.
︙ | ︙ | |||
380 381 382 383 384 385 386 | lappend lambda {set a 1} interp create slave slave eval [list apply $lambda foo] interp delete slave unset lambda } {} | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | lappend lambda {set a 1} interp create slave slave eval [list apply $lambda foo] interp delete slave unset lambda } {} test proc-8.1 {Auto argument linking} -body { proc P {*a} { set a 1 return } apply {{} { set a {} P a set a }} } -cleanup { rename P {} } -result 1 test proc-8.2 {Auto argument linking, multiple} -body { proc P {*a *b} { set a 1 set b 2 return } apply {{} { set a {} set b {} P a b set b }} } -cleanup { rename P {} } -result 2 test proc-8.3 {Auto argument linking, multiple of same} -body { proc P {*a *a} { set a 1 return } apply {{} { set a {} P a a set a }} } -cleanup { rename P {} } -result 1 test proc-8.4 {Auto argument linking, and defaults} -body { proc P {*a {foo bar} args} { return $foo } apply {{} { set a {} P a }} } -cleanup { rename P {} } -result {bar} test proc-8.5 {Auto argument linking, and args} -body { proc P {*a args} { return [lindex $args 0] } apply {{} { set a {} P a foo }} } -cleanup { rename P {} } -result {foo} test proc-8.6 {Auto argument linking, chain linking} -body { proc P {*a} { P2 a } proc P2 {*a} { incr a } apply {{} { P a set a }} } -cleanup { rename P {} rename P2 {} } -result {1} test proc-8.7 {Auto argument linking, create var in caller} -body { proc P {*a} { incr a } apply {{} { P a set a }} } -cleanup { rename P {} } -result {1} test proc-8.8 {Auto argument linking, default for auto-link formal} -body { proc P {{*a b}} { incr a } apply {{} { set a 0 P a }} } -constraints procbodytest -returnCodes error -cleanup { catch {rename P {}} } -result {procedure "P": formal parameter "*a" is to be a link and can't have a default value} test proc-8.9 {Auto argument linking, bad variable} -body { proc P {*a} { incr a } P mumbo::jumbo } -constraints procbodytest -returnCodes error -cleanup { catch {rename P {}} } -result {can't access "mumbo::jumbo": parent namespace doesn't exist} # cleanup catch {rename p ""} catch {rename t ""} ::tcltest::cleanupTests return |
︙ | ︙ |