What does "context" do?

Found this:

context
-> Frame (0x556feb14c198) of Interpreter:functionCompileContext

This came up because I’m working on some code that uses the variable “context,” and didn’t notice it was a special word because it was shadowed by local scope. While refactoring some things I accidentally used “context” without an accompanying var statement and started getting wacky errors.

Reassigning “context” is syntactically valid but it seems to crash sclang.

context = 3
// crash

This leads to a few questions. Anyone know what this reserved word does? Is it useful in any way? Should it be highlighted as a pseudo-variable in the SCIDE, or should it be removed?

Took a quick look at { context }.def.dumpByteCodes and found that it’s handled by a special byte code 0x11 or decimal 17. (I didn’t check the byte code for assigning to it.) The byte code is about getting an instance var. The only gettable instance vars named context are in DebugFrame and FunctionDef.

The latter provides access to variables declared in an enclosing scope.

f = {
    var list = List.new;
    10.do { |i| list.add(i) };
    list
};

In this case, the do function is an open function, and its use of list from the outer scope requires a reference to the outer scope. I’m pretty sure this is what context is for.

Since tampering with the context crashes the language, I can’t see that this is meant to be user-facing in any way.

So the next questions I’d ask are:

  • Does the compiler emit these byte codes for any reason other than an undeclared use of context?
  • Or, are there any places in the interpreter that invoke these byte codes without them being generated? (Unlikely, but who knows?)

hjh

Should it be highlighted as a pseudo-variable in the SCIDE, or should it be removed?

It’s an interpreter variable, like the single letters.

The comment says “Faked interpreter context frame. Don’t mess with it.” I guess best to follow the instructions!

I.e.

https://github.com/supercollider/supercollider/blob/develop/SCClassLibrary/Common/Core/Kernel.sc#L592

Ah, a getter-less variable in Interpreter… I’d seen that the source code hard-codes a slot index of 1 for this, and context is the second instance variable. So it must be this one.

I’m still not sure though, if we are not to mess with it, then why does the compiler make it available? It would be easier for the compiler not to have an exceptional case for this one keyword, and reject code blocks using context without declaring it. Instead it recognizes the keyword and emits a special byte code for it (i.e. an extra code branch to maintain). Perhaps it’s a vestige of JMc’s early debugging of the interpreter.

One thing I am sure of is that the user gains nothing from playing around with this (unless it’s a deep dive into interpreter internals, but even then only maybe).

hjh

I’m still not sure though, if we are not to mess with it, then why does the compiler make it available?

All of the Interpreter variables are visible?

(The getter/setter methods just allow “s == this.s” &etc., i.e. “this.context” is an error…)

The comment at the top of the Kernel.sc file says:

you must not make any change at all to the order or number of instance variables in these classes!

Perhaps these definitions need to mirror the VM structures that implement them?

Anyways, I only meant that regards documentation and syntax highlighting, I think “context” belongs with the Interpreter variables, not with the pseudo variables.

That does seem to be the case:

(
Interpreter.instVarNames.do { |name, i|
	// funny: "{ a }".compile is a function returning a function
	// because 'compile' wraps in a function,
	// we can just compile the name directly
	var func = name.asString.compile;
	[i, name, func.def.code].postln;
}; ""
)

[ 0, cmdLine, Int8Array[ 16, -14 ] ]
[ 1, context, Int8Array[ 17, -14 ] ]
[ 2, a, Int8Array[ 18, -14 ] ]
[ 3, b, Int8Array[ 19, -14 ] ]
[ 4, c, Int8Array[ 20, -14 ] ]
[ 5, d, Int8Array[ 21, -14 ] ]
[ 6, e, Int8Array[ 22, -14 ] ]
[ 7, f, Int8Array[ 23, -14 ] ]
[ 8, g, Int8Array[ 24, -14 ] ]
[ 9, h, Int8Array[ 25, -14 ] ]
[ 10, i, Int8Array[ 26, -14 ] ]
[ 11, j, Int8Array[ 27, -14 ] ]
[ 12, k, Int8Array[ 28, -14 ] ]
[ 13, l, Int8Array[ 29, -14 ] ]
[ 14, m, Int8Array[ 30, -14 ] ]
[ 15, n, Int8Array[ 31, -14 ] ]
[ 16, o, Int8Array[ 1, 16, -14 ] ]
[ 17, p, Int8Array[ 1, 17, -14 ] ]
[ 18, q, Int8Array[ 1, 18, -14 ] ]
[ 19, r, Int8Array[ 1, 19, -14 ] ]
[ 20, s, Int8Array[ 1, 20, -14 ] ]
[ 21, t, Int8Array[ 1, 21, -14 ] ]
[ 22, u, Int8Array[ 1, 22, -14 ] ]
[ 23, v, Int8Array[ 1, 23, -14 ] ]
[ 24, w, Int8Array[ 1, 24, -14 ] ]
[ 25, x, Int8Array[ 1, 25, -14 ] ]
[ 26, y, Int8Array[ 1, 26, -14 ] ]
[ 27, z, Int8Array[ 1, 27, -14 ] ]
[ 28, codeDump, Int8Array[ 1, 28, -14 ] ]
[ 29, preProcessor, Int8Array[ 1, 29, -14 ] ]

And the byte codes follow a strict numeric sequence (with overflow at o).

OK, then – documentation issue – though one could make a case that context is especially dangerous, and the compiler should probably fail if that keyword is not declared as a variable. (Even cmdLine = 4 won’t break anything, but context = \x and you’re crashing. It is reasonable to try to prevent accidental crashes.)

Yes, there are several classes marked as “intrinsic” in the C++ sources.

hjh