Tcl Source Code

Check-in [408dde56d0]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:merge novem
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dgp-refactor
Files: files | file ages | folders
SHA1: 408dde56d0b7d5121e5eaf4d315da57f022996bd
User & Date: dgp 2015-03-24 15:18:11
Context
2015-03-24
18:38
merge novem check-in: cc8a52a073 user: dgp tags: dgp-refactor
15:18
merge novem check-in: 408dde56d0 user: dgp tags: dgp-refactor
15:13
merge trunk check-in: 199d4d8540 user: dgp tags: novem
2015-03-12
15:07
merge novem check-in: 6edb8b4e49 user: dgp tags: dgp-refactor
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tcl.h.

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#define TCL_MINOR_VERSION   0
#define TCL_RELEASE_LEVEL   TCL_ALPHA_RELEASE
#define TCL_RELEASE_SERIAL  0

#define TCL_VERSION	    "9.0"
#define TCL_PATCH_LEVEL	    "9.0a0"

/*
 * STRICT: See MSDN Article Q83456
 */

#ifdef _WIN32
#   ifndef STRICT
#	define STRICT
#   endif
#endif /* _WIN32 */

/*
 * A special definition used to allow this header file to be included from
 * windows resource files so that they can obtain version information.
 * RC_INVOKED is defined by default by the windows RC tool.
 *
 * Resource compilers don't like all the C stuff, like typedefs and function
 * declarations, that occur below, so block them out.







<
<
<
<
<
<
<
<
<
<







57
58
59
60
61
62
63










64
65
66
67
68
69
70
#define TCL_MINOR_VERSION   0
#define TCL_RELEASE_LEVEL   TCL_ALPHA_RELEASE
#define TCL_RELEASE_SERIAL  0

#define TCL_VERSION	    "9.0"
#define TCL_PATCH_LEVEL	    "9.0a0"











/*
 * A special definition used to allow this header file to be included from
 * windows resource files so that they can obtain version information.
 * RC_INVOKED is defined by default by the windows RC tool.
 *
 * Resource compilers don't like all the C stuff, like typedefs and function
 * declarations, that occur below, so block them out.

Changes to generic/tclBasic.c.

3746
3747
3748
3749
3750
3751
3752
3753

3754
3755
3756
3757
3758
3759
3760
3761
				 * here, otherwise the pointer to the
				 * requested Command struct to be invoked. */
{
    Interp *iPtr = (Interp *) interp;

    /*
     * data[1] stores a marker for use by tailcalls; it will be set to 1 by
     * command redirectors (imports, alias, ensembles) so that tailcalls

     * finishes the source command and not just the target.
     */

    if (iPtr->deferredCallbacks) {
        iPtr->deferredCallbacks = NULL;
    } else {
	TclNRAddCallback(interp, NRCommand, NULL, NULL, NULL, NULL);
    }







|
>
|







3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
				 * here, otherwise the pointer to the
				 * requested Command struct to be invoked. */
{
    Interp *iPtr = (Interp *) interp;

    /*
     * data[1] stores a marker for use by tailcalls; it will be set to 1 by
     * command redirectors (imports, alias, ensembles) so that tailcall skips
     * this callback (that marks the end of the target command) and goes back
     * to the end of the source command. 
     */

    if (iPtr->deferredCallbacks) {
        iPtr->deferredCallbacks = NULL;
    } else {
	TclNRAddCallback(interp, NRCommand, NULL, NULL, NULL, NULL);
    }
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
    int result)
{
    Interp *iPtr = (Interp *) interp;

    iPtr->numLevels--;

     /*
      * If there is a tailcall, schedule it
      */
 
    if (data[1] && (data[1] != INT2PTR(1))) {
        TclNRAddCallback(interp, TclNRTailcallEval, data[1], NULL, NULL, NULL);
    }

    /* OPT ??







|







3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
    int result)
{
    Interp *iPtr = (Interp *) interp;

    iPtr->numLevels--;

     /*
      * If there is a tailcall, schedule it next
      */
 
    if (data[1] && (data[1] != INT2PTR(1))) {
        TclNRAddCallback(interp, TclNRTailcallEval, data[1], NULL, NULL, NULL);
    }

    /* OPT ??
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449

7450
7451
7452


7453
7454
7455
7456
7457
7458
7459
7460

7461
7462





7463
7464
7465
7466
7467
7468
7469
    int flags)
{
    return TclNREvalObjv(interp, objc, objv, flags|TCL_EVAL_NOERR,
	    (Command *) cmd);
}

/*****************************************************************************
 * Stuff for tailcalls
 *****************************************************************************
 *
 * Just to show that IT CAN BE DONE! The precise semantics are not simple,
 * require more thought. Possibly need a new Tcl return code to do it right?
 * Questions include:
 *   (1) How is the objc/objv tailcall to be run? My current thinking is that
 *	 it should essentially be

 *	     [tailcall a b c] <=> [uplevel 1 [list a b c]]
 *	 with two caveats
 *	     (a) the current frame is dropped first, after running all pending


 *		 cleanup tasks and saving its namespace
 *	     (b) 'a' is looked up in the returning frame's namespace, but the
 *		 command is run in the context to which we are returning
 *	 Current implementation does this if [tailcall] is called from within
 *	 a proc, errors otherwise.
 *   (2) Should a tailcall bypass [catch] in the returning frame? Current
 *	 implementation does not (or does it? Changed, test!) - it causes an
 *	 error.

 *
 * FIXME NRE!





 */

void
TclMarkTailcall(
    Tcl_Interp *interp)
{
    Interp *iPtr = (Interp *) interp;







|


<
<
<
|
<
>
|
<
|
>
>
|
<
|
<
<
<
<
<
>

<
>
>
>
>
>







7436
7437
7438
7439
7440
7441
7442
7443
7444
7445



7446

7447
7448

7449
7450
7451
7452

7453





7454
7455

7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
    int flags)
{
    return TclNREvalObjv(interp, objc, objv, flags|TCL_EVAL_NOERR,
	    (Command *) cmd);
}

/*****************************************************************************
 * Tailcall related code
 *****************************************************************************
 *



 * The steps of the tailcall dance are as follows:

 *
 *   1. when [tailcall] is invoked, it stores the corresponding callback in

 *      the current CallFrame and returns TCL_RETURN
 *   2. when the CallFrame is popped, it calls TclSetTailcall to store the
 *      callback in the proper NRCommand callback - the spot where the command
 *      that pushed the CallFrame is completely cleaned up

 *   3. when the NRCommand callback runs, it schedules the tailcall callback





 *      to run immediately after it returns
 *

 *   One delicate point is to properly define the NRCommand where the tailcall
 *   will execute. There are functions whose purpose is to help define the
 *   precise spot: TclMarkTailcall ("this is the spot") and TclSkipTailcall
 *   ("skip the next command: we are redirecting to it, tailcalls should run
 *   after WE return"), TclPushTailcallPoint (special for OO).
 */

void
TclMarkTailcall(
    Tcl_Interp *interp)
{
    Interp *iPtr = (Interp *) interp;
7489
7490
7491
7492
7493
7494
7495












7496
7497
7498
7499
7500
7501
7502
TclPushTailcallPoint(
    Tcl_Interp *interp)
{
    TclNRAddCallback(interp, NRCommand, NULL, NULL, NULL, NULL);
    ((Interp *) interp)->numLevels++;
}













void
TclSetTailcall(
    Tcl_Interp *interp,
    Tcl_Obj *listPtr)
{
    /*
     * Find the splicing spot: right before the NRCommand of the thing







>
>
>
>
>
>
>
>
>
>
>
>







7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
7498
7499
7500
7501
7502
7503
7504
7505
7506
7507
7508
7509
7510
7511
7512
TclPushTailcallPoint(
    Tcl_Interp *interp)
{
    TclNRAddCallback(interp, NRCommand, NULL, NULL, NULL, NULL);
    ((Interp *) interp)->numLevels++;
}


/*
 *----------------------------------------------------------------------
 *
 * TclSetTailcall --
 *
 *	Splice a tailcall command in the proper spot of the NRE callback
 *	stack, so that it runs at the right time.
 *
 *----------------------------------------------------------------------
 */

void
TclSetTailcall(
    Tcl_Interp *interp,
    Tcl_Obj *listPtr)
{
    /*
     * Find the splicing spot: right before the NRCommand of the thing
7513
7514
7515
7516
7517
7518
7519

















7520
7521
7522
7523
7524
7525
7526
7527
7528
7529
7530
7531
7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
7557
7558
7559
7560
7561
7562
    }
    if (!runPtr) {
        Tcl_Panic("tailcall cannot find the right splicing spot: should not happen!");
    }
    runPtr->data[1] = listPtr;
}


















int
TclNRTailcallObjCmd(
    ClientData clientData,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    Interp *iPtr = (Interp *) interp;

    if (objc < 1) {
	Tcl_WrongNumArgs(interp, 1, objv, "?command? ?arg ...?");
	return TCL_ERROR;
    }

    if (!(iPtr->varFramePtr->isProcCallFrame & 1)) {	/* or is upleveled */
        Tcl_SetObjResult(interp, Tcl_NewStringObj(
                "tailcall can only be called from a proc or lambda", -1));
        Tcl_SetErrorCode(interp, "TCL", "TAILCALL", "ILLEGAL", NULL);
	return TCL_ERROR;
    }

    /*
     * Invocation without args just clears a scheduled tailcall; invocation
     * with an argument replaces any previously scheduled tailcall.
     */

    if (iPtr->varFramePtr->tailcallPtr) {
        Tcl_DecrRefCount(iPtr->varFramePtr->tailcallPtr);
        iPtr->varFramePtr->tailcallPtr = NULL;
    }

    /*
     * Create the callback to actually evaluate the tailcalled
     * command, then set it in the varFrame so that PopCallFrame can use it
     * at the proper time. Being lazy: exploit the TclNRAddCallBack macro to
     * build the callback.
     */

    if (objc > 1) {
        Tcl_Obj *listPtr, *nsObjPtr;
        Tcl_Namespace *nsPtr = (Tcl_Namespace *) iPtr->varFramePtr->nsPtr;
        Tcl_Namespace *ns1Ptr;








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>














|

|

















|
<







7523
7524
7525
7526
7527
7528
7529
7530
7531
7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
7557
7558
7559
7560
7561
7562
7563
7564
7565
7566
7567
7568
7569
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581

7582
7583
7584
7585
7586
7587
7588
    }
    if (!runPtr) {
        Tcl_Panic("tailcall cannot find the right splicing spot: should not happen!");
    }
    runPtr->data[1] = listPtr;
}


/*
 *----------------------------------------------------------------------
 *
 * TclNRTailcallObjCmd --
 *
 *	Prepare the tailcall as a list and store it in the current
 *	varFrame. When the frame is later popped the tailcall will be spliced
 *	at the proper place.
 *
 * Results:
 *	The first NRCommand callback that is not marked to be skipped is
 *	updated so that its data[1] field contains the tailcall list.
 *
 *----------------------------------------------------------------------
 */

int
TclNRTailcallObjCmd(
    ClientData clientData,
    Tcl_Interp *interp,
    int objc,
    Tcl_Obj *const objv[])
{
    Interp *iPtr = (Interp *) interp;

    if (objc < 1) {
	Tcl_WrongNumArgs(interp, 1, objv, "?command? ?arg ...?");
	return TCL_ERROR;
    }

    if (!(iPtr->varFramePtr->isProcCallFrame & 1)) {
        Tcl_SetObjResult(interp, Tcl_NewStringObj(
                "tailcall can only be called from a proc, lambda or method", -1));
        Tcl_SetErrorCode(interp, "TCL", "TAILCALL", "ILLEGAL", NULL);
	return TCL_ERROR;
    }

    /*
     * Invocation without args just clears a scheduled tailcall; invocation
     * with an argument replaces any previously scheduled tailcall.
     */

    if (iPtr->varFramePtr->tailcallPtr) {
        Tcl_DecrRefCount(iPtr->varFramePtr->tailcallPtr);
        iPtr->varFramePtr->tailcallPtr = NULL;
    }

    /*
     * Create the callback to actually evaluate the tailcalled
     * command, then set it in the varFrame so that PopCallFrame can use it
     * at the proper time.

     */

    if (objc > 1) {
        Tcl_Obj *listPtr, *nsObjPtr;
        Tcl_Namespace *nsPtr = (Tcl_Namespace *) iPtr->varFramePtr->nsPtr;
        Tcl_Namespace *ns1Ptr;

7573
7574
7575
7576
7577
7578
7579











7580
7581
7582
7583
7584
7585
7586
 	TclListObjSetElement(interp, listPtr, 0, nsObjPtr);
        
        iPtr->varFramePtr->tailcallPtr = listPtr;
    }
    return TCL_RETURN;
}












int
TclNRTailcallEval(
    ClientData data[],
    Tcl_Interp *interp,
    int result)
{
    Interp *iPtr = (Interp *) interp;







>
>
>
>
>
>
>
>
>
>
>







7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
7619
7620
7621
7622
7623
 	TclListObjSetElement(interp, listPtr, 0, nsObjPtr);
        
        iPtr->varFramePtr->tailcallPtr = listPtr;
    }
    return TCL_RETURN;
}


/*
 *----------------------------------------------------------------------
 *
 * TclNRTailcallEval --
 *
 *	This NREcallback actually causes the tailcall to be evaluated.
 *
 *----------------------------------------------------------------------
 */

int
TclNRTailcallEval(
    ClientData data[],
    Tcl_Interp *interp,
    int result)
{
    Interp *iPtr = (Interp *) interp;

Changes to generic/tclExecute.c.

3049
3050
3051
3052
3053
3054
3055

3056
3057
3058
3059
3060
3061
3062
3063
3064
	iPtr->ensembleRewrite.sourceObjs = objv;
	iPtr->ensembleRewrite.numRemovedObjs = opnd;
	iPtr->ensembleRewrite.numInsertedObjs = 1;
	DECACHE_STACK_INFO();
	pc += 6;
	TEBC_YIELD();


	TclNRAddCallback(interp, TclClearRootEnsemble, NULL,NULL,NULL,NULL);
	TclSkipTailcall(interp);
	return TclNREvalObjEx(interp, objPtr, TCL_EVAL_INVOKE, NULL, INT_MIN);

    /*
     * -----------------------------------------------------------------
     *	   Start of INST_LOAD instructions.
     *
     * WARNING: more 'goto' here than your doctor recommended! The different







>

<







3049
3050
3051
3052
3053
3054
3055
3056
3057

3058
3059
3060
3061
3062
3063
3064
	iPtr->ensembleRewrite.sourceObjs = objv;
	iPtr->ensembleRewrite.numRemovedObjs = opnd;
	iPtr->ensembleRewrite.numInsertedObjs = 1;
	DECACHE_STACK_INFO();
	pc += 6;
	TEBC_YIELD();

	TclMarkTailcall(interp);
	TclNRAddCallback(interp, TclClearRootEnsemble, NULL,NULL,NULL,NULL);

	return TclNREvalObjEx(interp, objPtr, TCL_EVAL_INVOKE, NULL, INT_MIN);

    /*
     * -----------------------------------------------------------------
     *	   Start of INST_LOAD instructions.
     *
     * WARNING: more 'goto' here than your doctor recommended! The different

Changes to tests/nre.test.

146
147
148
149
150
151
152





















153
154
155
156
157
158
159
    a 0
} -cleanup {
    rename a {}
    rename b {}
} -constraints {
    testnrelevels
} -result {{0 2 1 1} 0}






















test nre-5.1 {[namespace eval] is not recursive} -setup {
    namespace eval ::foo {
	setabs
    }
    proc foo::a i [makebody {namespace eval ::foo [list a $i]}]
} -body {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
    a 0
} -cleanup {
    rename a {}
    rename b {}
} -constraints {
    testnrelevels
} -result {{0 2 1 1} 0}

test nre-4.2 {(compiled) ensembles do not break tailcall} -setup {
    # Fix Bug d87cb18205
    proc b {} {
	tailcall append result first
    }
    set map [namespace ensemble configure ::dict -map]
    dict set map a b
    namespace ensemble configure ::dict -map $map
    proc demo {} {
	dict a
	append result second
    }
} -body {
    demo
} -cleanup {
    rename demo {}
    namespace ensemble configure ::dict -map [dict remove $map a]
    unset map
    rename b {}
} -result firstsecond

test nre-5.1 {[namespace eval] is not recursive} -setup {
    namespace eval ::foo {
	setabs
    }
    proc foo::a i [makebody {namespace eval ::foo [list a $i]}]
} -body {

Changes to tests/tailcall.test.

142
143
144
145
146
147
148






























149
150
151
152
153
154
155
    namespace ensemble create -command a -map {b b}
} -body {
    a b 0
} -cleanup {
    rename a {}
    rename b {}
} -result {0 0 0 0 0 0}































test tailcall-0.6 {tailcall is constant space} -constraints {testnrelevels knownBug} -setup {
    #
    # This test fails because ns-unknown is not NR-enabled
    #
    proc c i {
	if {$i == 1} {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
    namespace ensemble create -command a -map {b b}
} -body {
    a b 0
} -cleanup {
    rename a {}
    rename b {}
} -result {0 0 0 0 0 0}

test tailcall-0.5.1 {tailcall is constant space} -constraints testnrelevels -setup {
    # 
    # This test is related to [bug d87cb182053fd79b3]: the fix to that bug was
    # to remove a call to TclSkipTailcall, which caused a violation of the
    # constant-space property of tailcall in that particular
    # configuration. This test was added to detect that, and insure that the
    # problem is fixed.
    #

    proc b i {
	if {$i == 1} {
	    depthDiff
	}
	if {[incr i] > 10} {
	    return [depthDiff]
	}
	tailcall dict b $i
    }
    set map0 [namespace ensemble configure dict -map]
    set map $map0
    dict set map b b
    namespace ensemble configure dict -map $map
} -body {
    dict b 0
} -cleanup {
    rename b {}
    namespace ensemble configure dict -map $map0
    unset map map0
} -result {0 0 0 0 0 0}

test tailcall-0.6 {tailcall is constant space} -constraints {testnrelevels knownBug} -setup {
    #
    # This test fails because ns-unknown is not NR-enabled
    #
    proc c i {
	if {$i == 1} {

Changes to win/tclAppInit.c.

12
13
14
15
16
17
18

19

20
21
22
23
24
25
26
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tcl.h"
#define WIN32_LEAN_AND_MEAN

#include <windows.h>

#undef WIN32_LEAN_AND_MEAN
#include <locale.h>
#include <stdlib.h>
#include <tchar.h>

#ifdef TCL_TEST
extern Tcl_PackageInitProc Tcltest_Init;







>

>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tcl.h"
#define WIN32_LEAN_AND_MEAN
#define STRICT			/* See MSDN Article Q83456 */
#include <windows.h>
#undef STRICT
#undef WIN32_LEAN_AND_MEAN
#include <locale.h>
#include <stdlib.h>
#include <tchar.h>

#ifdef TCL_TEST
extern Tcl_PackageInitProc Tcltest_Init;

Changes to win/tclWinConsole.c.

216
217
218
219
220
221
222











223
224

225
226
227
228
229
230
231
    DWORD nbytes,
    LPDWORD nbytesread)
{
    DWORD ntchars;
    BOOL result;
    int tcharsize = sizeof(TCHAR);












    result = ReadConsole(hConsole, lpBuffer, nbytes / tcharsize, &ntchars,
	    NULL);

    if (nbytesread != NULL) {
	*nbytesread = ntchars * tcharsize;
    }
    return result;
}

static BOOL







>
>
>
>
>
>
>
>
>
>
>
|
|
>







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
    DWORD nbytes,
    LPDWORD nbytesread)
{
    DWORD ntchars;
    BOOL result;
    int tcharsize = sizeof(TCHAR);

    /*
     * If user types a Ctrl-Break or Ctrl-C, ReadConsole will return
     * success with ntchars == 0 and GetLastError() will be
     * ERROR_OPERATION_ABORTED. We do not want to treat this case
     * as EOF so we will loop around again. If no Ctrl signal handlers
     * have been established, the default signal OS handler in a separate 
     * thread will terminate the program. If a Ctrl signal handler
     * has been established (through an extension for example), it
     * will run and take whatever action it deems appropriate.
     */
    do {
        result = ReadConsole(hConsole, lpBuffer, nbytes / tcharsize, &ntchars,
                             NULL);
    } while (result && ntchars == 0 && GetLastError() == ERROR_OPERATION_ABORTED);
    if (nbytesread != NULL) {
	*nbytesread = ntchars * tcharsize;
    }
    return result;
}

static BOOL