I followed this https://wiki.gnome.org/Projects/GnomeShell/Extensions/StepByStepTutorial for Overwriting a function.
For example, I want to override the function _setupKeyboard() on the Keyboard class, but my override isn't invoked. The specific portion I want to change is this, to remove the if guard:
if (Meta.is_wayland_compositor()) {
this._connectSignal(this._keyboardController, 'emoji-visible',
this._onEmojiKeyVisible.bind(this));
}
I copied the function from the source, removed the part I didn't want, then set the replacement function like this:
const Keyboard = imports.ui.keyboard;
Keyboard.Keyboard.prototype._setupKeyboard = myOverride;
Why isn't my override being invoked and how can I achieve this?
There are two common reasons an override won't be invoked. If the method is invoked before your override is applied, or if the function is a callback set with Function.prototype.bind() which creates a new closure.
In this case, the function _setupKeyboard() is called before your override is applied. When GNOME Shell starts up, it creates an instance of Keyboard.KeyboardManager here:
// main.js, line #204
keyboard = new Keyboard.KeyboardManager();
By the time the keyboard variable has been assigned to the instance, a default Keyboard.Keyboard class has been created and the function _setupKeyboard() has already been called in Keyboard._init(), which is much sooner than your extension is loaded.
Since there's no way to easily fix that, your best option is to just re-create the one part of the code you want to run:
const Meta = imports.gi.Meta;
const Main = imports.ui.main;
const Keyboard = imports.ui.keyboard.Keyboard;
const originalSetup = Keyboard.prototype._setupKeyboard;
const modifiedSetup = function () {
originalSetup.call(this);
if (!Meta.is_wayland_compositor()) {
this._connectSignal(this._keyboardController, 'emoji-visible',
this._onEmojiKeyVisible.bind(this));
}
this._relayout();
};
function init() {
}
// Your extension's enable function (might be a class method)
function enable() {
let kbd = Main.keyboard.keyboardActor;
if (kbd !== null) {
if (!Meta.is_wayland_compositor()) {
kbd.__mySignalId = kbd._connectSignal(kbd._keyboardController, 'emoji-visible',
kbd._onEmojiKeyVisible.bind(kbd));
}
}
Keyboard.prototype._setupKeyboard = modifiedSetup;
}
function disable() {
let kbd = Main.keyboard.keyboardActor;
if (kbd !== null && kbd.__mySignalId) {
kbd.disconnect(kbd.__mySignalId);
kbd.__mySignalId = 0;
}
Keyboard.prototype._setupKeyboard = originalSetup;
}
This is not very pretty, but that is often the price of patching private code. I can also not guarantee that the code will do what you want, because I suspect the emoji key is hidden on X11 for a reason.
Related
The problem is rather simple. We need to imbue a function with a parameter, and then simply extract that parameter from the body of the function. I'll present the outline in typescript...
abstract class Puzzle {
abstract assign(param, fn): any;
abstract getAssignedValue(): any;
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
Let's set the scene:
Assume strict mode, no arguments or callee. Should work reasonably well on the recent-ish version of v8.
The function passed to assign() must be an anonymous arrow function that doesn't take any parameters.
... and it's alsoasync. The assigned value could just be stored somewhere for the duration of the invocation, but because the function is async and can have awaits, you can't rely on the value keeping through multiple interleaved invocations.
this.getAssignedValue() takes no parameters, returning whatever we assigned with the assign() method.
Would be great to find a more elegant solution that those I've presented below.
Edit
Okay, we seem to have found a good solid solution inspired by zone.js. The same type of problem is solved there, and the solution is to override the meaning of some system-level primitives, such as SetTimeout and Promise. The only headache above was the async statement, which meant that the body of the function could be effectively reordered. Asyncs are ultimately triggered by promises, so you'll have to override your Promise with something that is context aware. It's quite involved, and because my use case is outside of browser or even node, I won't bore you with details. For most people hitting this kind of problem - just use zone.js.
Hacky Solution 2
class HackySolution2 extends Puzzle {
assign(param: any, fn: AnyFunction): AnyFunction {
const sub = Object(this);
sub["getAssignedValue"] = () => param;
return function () { return eval(fn.toString()); }.call(sub);
}
getAssignedValue() {
return undefined;
}
}
In this solution, I'm making an object that overrides the getAssignedValue() method, and re-evaluates the source code of the passed function, effectively changing the meaning of this. Still not quite production grade...
Edit.
Oops, this breaks closures.
I don't know typescript so possibly this isn't useful, but what about something like:
const build_assign_hooks = () => {
let assignment;
const get_value = () => assignment;
const assign = (param, fn) => {
assignment = param;
return fn;
}
return [assign, get_value];
};
class Puzzle {
constructor() {
const [assign, getAssignedValue] = build_assign_hooks();
this.assign = assign;
this.getAssignedValue = getAssignedValue;
}
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
const puzzle = new Puzzle();
puzzle.test();
Hacky Solution 1
We actually have a working implementation. It's such a painful hack, but proves that this should be possible. Somehow. Maybe there's even a super simple solution that I'm missing just because I've been staring at this for too long.
class HackySolution extends Puzzle {
private readonly repo = {};
assign(param: any, fn) {
// code is a random field for repo. It must also be a valid JS fn name.
const code = 'd' + Math.floor(Math.random() * 1000001);
// Store the parameter with this code.
this.repo[code] = param;
// Create a function that has code as part of the name.
const name = `FN_TOKEN_${code}_END_TOKEN`;
const wrapper = new Function(`return function ${name}(){ return this(); }`)();
// Proceed with normal invocation, sending fn as the this argument.
return () => wrapper.call(fn);
}
getAssignedValue() {
// Comb through the stack trace for our FN_TOKEN / END_TOKEN pair, and extract the code.
const regex = /FN_TOKEN_(.*)_END_TOKEN/gm;
const code = regexGetFirstGroup(regex, new Error().stack);
return this.repo[code];
}
}
So the idea in our solution is to examine the stack trace of the new Error().stack, and wrap something we can extract as a token, which in turn we'll put into a repo. Hacky? Very hacky.
Notes
Testing shows that this is actually quite workable, but requires a more modern execution environment than we have - i.e. ES2017+.
In vanilla JS, my code would work fine. For this case, I'd like to componentize my Wall class which's supposed to display the image in the browser that the user has uploaded. Again, this works normally in vanilla JS but not JSX.
I'm getting a potentially invalid reference access to a class field via this in a nested function on the document.querySelector("#file-input").addEventListener("change", this.previewImages); line which I think is causing the issue.
What am I doing wrong and how can I fix it?
import React, {Component} from 'react';
class Wall extends Component {
constructor(props) {
super(props);
this.previewImages = this.previewImages.bind(this);
}
previewImages() {
let preview = document.createElement("div");
if (this.files) {
[].forEach().call(this.files, readAndPreview());
}
function readAndPreview() {
if (!/\.(jpe?g|png|gif)$/i.test(file.name)) {
return alert(file.name + " is not an image");
}
let reader = new FileReader();
reader.addEventListener("load", () => {
let image = new Image();
image.height = 100;
image.title = file.name;
image.src = this.result;
let date = Date.now();
let d = new Date(parseInt(date, 10));
let ds = d.toString("MM/dd/yy HH:mm:ss");
console.log(ds);
let initialCountOfLikes = 0;
let zeroLikes = document.createElement("h1");
let zeroLikesTextNode = zeroLikes.createTextNode(initialCountOfLikes + " likes");
zeroLikes.appendChild(zeroLikesTextNode);
preview.appendChild(image); // makes image appear
preview.appendChild(zeroLikes); // makes like count appear
image.ondblclick = function() {
if (initialCountOfLikes === 0) {
console.log("Inside if block");
initialCountOfLikes++;
console.log("initialCountOfLikes++ => " + initialCountOfLikes);
} else if (initialCountOfLikes === 1) {
console.log("inside second else if block");
initialCountOfLikes--;
console.log("initialCountOfLikes-- => " + initialCountOfLikes);
}
zeroLikesTextNode.nodeValue = initialCountOfLikes + " likes";
};
});
reader.readAsDataURL(file);
document.querySelector("#file-input").addEventListener("change", this.previewImages);
}
}
render() {
return (
<div id="file-input-wrapper">
<input type="file" />
<label htmlFor="file-input" id={"LblBrowse"} />
</div>
);
}
}
export default Wall;
The warning is telling you that using this in JavaScript frequently has confusing implications, specifically when used inside a function nested inside another function. this stops referring to the class, and instead refers to the scope of your nested function.
In your case, this probably is a legitimate problem (I think) because you have your class, Wall, which has a method previewImages() and a property files. Within that function, you have instantiated a new function, readAndPreview(), inside which you specify this.previewImages as a function callback to the addEventListener function.
They're saying you're potentially using this.previewImages incorrectly, because you're writing functions in traditional JavaScript syntax, function foo() { ... }, where this keeps being redefined in each child function call. In your case, I believe that this is referring to the context of readAndPreview(), and hence cannot access the method this.previewImages() since this doesn't refer to your parent class, Wall.
People used to do things like, make a var that = this; on the parent class, and you'd know that that always meant the parent class.
But now, with ES6 lambda functions using the "fat arrow" syntax () => { } you can access this and know it's referring to the parent scope.
I believe you can refactor your class to change previewImages() { into previewImages = () => { and know that this will refer to the class. You'll have to do the same with function readAndPreview() {. Change it to const readAndPreview = () => {. If you're setting it to a variable, though, I think you'll have to move it above the place you call it, though. e.g. above
if (this.files) {
[].forEach().call(this.files, readAndPreview());
}
I faced this error in Angular 8.
I used the Arrow function instead of regular functions to solve.
In your case.
readAndPreview = () => { ... }
This might solve your problem.
Using of arrow function may help you. Arrow functions don't have their own bindings to this, arguments or super.
I have 3 classes, all extend the previous one.
Entity -> Body -> Player
Each one has a die() method which do very different things.
Entity.die() will call the db
Body.die() will animate the body
Player.die() will call the UI and play special sound.
I don't want to manually call Entity.die() inside Body.die method, mainly because I have many classes and many common methods and I don't want to forget something.
I wrote this little piece of code which does exactly this, the Error stack is easy to understand and points to the correct lines.
function overLoadMethods (parent, children) {
const methods = {}
for (let [fname, fn] of Object.entries(parent)) {
if (typeof fn === 'function') {
if (children[fname]) {
methods[fname] = function () {
fn()
children[fname]()
}
Object.defineProperty(methods[fname], 'name', { value: fname })
} else {
methods[fname] = fn
}
}
}
return methods
}
function createEntity () {
return {
die: () => {
console.log(new Error().stack)
console.log('entity die')
}
}
}
const bodyMethods = {
die: () => {
console.log(new Error().stack)
console.log('body die')
}
}
function createBody () {
const entity = createEntity()
const overLoadedMethods = overLoadMethods(entity, bodyMethods)
return {
...entity,
...bodyMethods,
...overLoadedMethods
}
}
const playerMethods = {
die: () => {
console.log(new Error().stack)
console.log('player die')
}
}
function createPlayer () {
const body = createBody()
const overLoadedMethods = overLoadMethods(body, playerMethods)
return {
...body,
...playerMethods,
...overLoadedMethods
}
}
const player = createPlayer()
// will call Entity.die() then Body.die() then Player.die()
player.die()
Everything is working fine but I never saw this pattern before and I guess there is a good reason which I'm unaware of.
Could someone point the weakness of this pattern if there is one (pretty sure there is) ?
Common Lisp has something similar. When you define a method in a derived class you can decide whether this method should be executed:
:before (i.e. the base method will be called automatically after specialized one)
:after (i.e. the base method will be called automatically before the specialized one)
:around (i.e. only the specialized method will be called, but inside its body you can call the base method with call-next-method that is a special syntax that allows calling base method with either the parameters specified by the caller or the parameters that you want to pass instead).
For example C++ only has around available for general methods (but without the ability to call the base version with original parameters) and forces instead use of before in constructor and after in destructors.
I understand the desire to not repeat code and create code that makes it hard to make mistakes and forget things. But you still have code the you need to remember to wire up. For example, instead of calling Entity.die() you need to call overLoadMethods(). I'm not sure that's an improvement over regular of classes and calling super.die().
You can get the chained method behavior using ES6 classes (you can also get it using prototypes). This has a lot of advantages:
• The pattern is baked into the language.
• It's very clear to see parent/child relationship
• There's a lot of commentary, theory, and examples of different patterns
class Entity {
die() {
// Entity-specific behavior
console.log('entity die')
}
}
class Body extends Entity {
die() {
super.die()
// Body-specific behavior
console.log('body die')
}
}
class Player extends Body {
die() {
super.die()
// Player-specific behavior
console.log('player die')
}
}
const player = new Player
// will call Entity.die() then Body.die() then Player.die()
player.die()
In my case, I'm using the Phaser framework.
So in this example I'm extending the Group class of phaser. Every 'actor' class (Sprite, Group, ...) calls upon the update() prototype every few miliseconds.
My idea was to extend this function only when the application runs on a desktop (so not on a phone).
for example:
var MousePointer = function (game, parent, name) {
Phaser.Group.call(this, game, parent, name);
this.init();
};
MousePointer.prototype = Object.create(Phaser.Group.prototype);
MousePointer.prototype.constructor = MousePointer;
MousePointer.prototype.init = function () {
// ... init
};
MousePointer.prototype.update = function () {
// Do something when on desktop
};
I can't possibly use an if clausule in the update() function to check whether the player is on dekstop/tablet/phone. So is there a way to actually override the prototype on initialisation?
for example (pseudocode):
if(onPhone)
MousePointer.prototype.update = parent.prototype.update;
else
MousePointer.prototype.update = this.update;
Well, you've kind of already written the answer for yourself, haven't you? This code (not inside the init method).
if(onPhone) {
MousePointer.prototype.update = function(){//Phone implementation};
} else {
MousePointer.prototype.update = function(){//Other implementation};
}
I advise against starting off with the "regular" function and then potentially overriding it, since you're just declaring it for nothing.
I think a better way to do this would be to write two different classes that shares the same parent, and then write different update() implementations for them. Then you can just do something like:
if(phone) {
var obj = new PhoneMousePointerObject();
} else {
var obj = new DesktopMousePointerObject();
}
// ... later
obj.update()
I am attempting to make a project which does two way data binding on two specified variables. However, when I tried it out, the project did not seem to be working.
I'm pretty sure that what I did wrong is that I specified a constructor, and then inside, I created a variable using "this," a function using "this," and then I tried to use the first variable inside the function using "this." Is this allowed?
The code for my project is in the snippet below.
function glue(varOne, varTwo, interval = 60) {
this.varOne = varOne;
this.varTwo = varTwo;
this.varOneClone = this.varOne;
this.varTwoClone = this.varTwo;
this.interval = interval;
this.onChange = function(changedVar) {
if (changedVar == this.varOne) {
this.varTwo = this.varOne;
} else if (changedVar == this.varTwo) {
this.varOne = this.varTwo;
}
this.varOneClone = this.varOne;
this.varTwoClone = this.varTwo;
};
this.intervalID = setInterval(function() {
if (this.varOne != this.varTwo) {
if (this.varOne != this.varOneClone) {
this.onChange(this.varOne);
} else if (this.varTwo != this.varTwoClone) {
this.onChange(this.varTwo);
}
}
}, this.interval);
this.clearUpdate = function() {
clearInterval(intervalID);
};
this.changeUpdate = function(newInterval) {
this.interval = newInterval;
clearInterval(intervalID);
this.intervalID = setInterval(function() {
if (this.varOne != this.varTwo) {
if (this.varOne != this.varOneClone) {
this.onChange(this.varOne);
} else if (this.varTwo != this.varTwoClone) {
this.onChange(this.varTwo);
}
}
}, this.interval);
};
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Glue</title>
</head>
<body>
<input id="input" type="text"></input>
<p id="output"></p>
<script>
var input = document.getElementById("input");
var output = document.getElementById("ouput");
var glue = new glue(input, output, 60);
</script>
</body>
</html>
Thank you!
Edit:
I've tried using the var self = this; method which two people recommended, but it still refuses to work. The error from the console is TypeError: glue is not a constructor, but I'm not sure why this happens. I want glue to be a constructor. Please help! The new code is below.
function glue(varOne, varTwo, interval = 60) {
var self = this;
self.varOne = varOne;
self.varTwo = varTwo;
self.varOneClone = self.varOne;
self.varTwoClone = self.varTwo;
self.interval = interval;
self.onChange = function(changedVar) {
if (changedVar == self.varOne) {
self.varTwo = self.varOne;
} else if (changedVar == self.varTwo) {
self.varOne = self.varTwo;
}
self.varOneClone = self.varOne;
self.varTwoClone = self.varTwo;
};
self.intervalID = setInterval(function() {
if (self.varOne != self.varTwo) {
if (self.varOne != self.varOneClone) {
self.onChange(self.varOne);
} else if (self.varTwo != self.varTwoClone) {
self.onChange(self.varTwo);
}
}
}, self.interval);
self.clearUpdate = function() {
clearInterval(intervalID);
};
self.changeUpdate = function(newInterval) {
self.interval = newInterval;
clearInterval(intervalID);
self.intervalID = setInterval(function() {
if (self.varOne != self.varTwo) {
if (self.varOne != self.varOneClone) {
self.onChange(self.varOne);
} else if (self.varTwo != self.varTwoClone) {
self.onChange(self.varTwo);
}
}
}, self.interval);
};
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Glue</title>
</head>
<body>
<input id="input" type="text"></input>
<p id="output"></p>
<script>
var input = document.getElementById("input");
var output = document.getElementById("ouput");
var glue = new glue(input, output, 60);
</script>
</body>
</html>
Thanks for your help!
There are few problems that you could fix, as you said, you're using this everywhere. this might not always point to the same object in some context so if you want to use the same object, you could use a variable accessible from the scope.
Usually, people use a variable named self:
var self = this
Then the self variable should be used everywhere where you want to access specifically that object. So in your onChange and setInterval, it would be a better idea to access this by using the self object.
One other way, would be to instead bind a bounded function. In this case, the this object will be the one you bound the function to.
this.onChange = (function () {
// here this will be equal to context
}).bind(context)
In this case, you can set context to this and within the function you can still use this without worrying which object will be this.
You can also make sure to call method using this.func.call(this, params...) or this.func.apply(this, paramArray).
There are a lot of ways to fix this, but if you search about bind, apply and call. It will be enough to understand how to make more complex construction for your code.
To give you an idea of how bind works, take this example:
function bind(method, obj) {
// Create a callable
return function () {
// Pass the arguments to the method but use obj as this instead
return method.apply(obj, arguments);
}
}
function func(a, b, c) {
console.log(this, a, b, c)
}
var bound1 = bind(func, {a: 1})
var bound2 = bind(func, {a: 2})
func(1,2,3)
bound1(3,4,5)
bound2(6,7,8)
You'll see that when func isn't called, this defaults to window. But in case of bound1 and bound2, it will be using the second parameter of bind.
All that to say that you can control which object will be used as this but if you don't specify which object is going to be used, then you might end up using window as this like in a setInterval. In other words, the keyword this isn't scoped like other variables. It depends of the context, not of its place in the code.
The problem is that the setInterval callback is made in a different context, so this will no longer be your glue object. Since you're running within a browser, it will be the window object. I'm pretty sure that all the other times you use this, it is referencing the correct object.
There are a few options to handle this. The first is to use the .bind() method. It is definitely the cleanest, and requires the least amount of "tweaking". However, it's not supported by IE8. Hopefully you don't have to support that browser, considering MS has dropped support for it, except for in embedded systems. Here's how it would work:
this.intervalID = setInterval(function(self) {
if (this.varOne != this.varTwo) {
if (this.varOne != this.varOneClone) {
this.onChange(this.varOne);
} else if (this.varTwo != this.varTwoClone) {
this.onChange(this.varTwo);
}
}
}.bind(this), this.interval);
Another option is to create a variable that holds the value of this before you call setInterval and then rely on the closure to give you access to that variable:
function glue(varOne, varTwo, interval = 60) {
var self = this;
//...
this.intervalID = setInterval(function() {
if (self.varOne != self.varTwo) {
if (self.varOne != self.varOneClone) {
self.onChange(self.varOne);
} else if (self.varTwo != self.varTwoClone) {
self.onChange(self.varTwo);
}
}
}, this.interval);
//...
}
Finally, you could also use an immediately-invoked function to pass in the value of this without creating a closure:
this.intervalID = setInterval((function(self) {
return function() {
if (self.varOne != self.varTwo) {
if (self.varOne != self.varOneClone) {
self.onChange(self.varOne);
} else if (self.varTwo != self.varTwoClone) {
self.onChange(self.varTwo);
}
}
}
})(this), this.interval);
EDIT:
Everything said above is a problem and the options given for solving it should fix that problem. However, it's one of only a few problems in your code sample.
There's also a problem with the line var glue = new glue(input, output, 60);. You can't have a variable with the same name as a function. If the function is declared first, as it should be in this case, then the variable overwrites it, so the function essentially no longer exists by the time you call new glue(). This is why you are getting the glue is not a constructor error.
I see that in your jsbin you've changed the variable name to tape. That fixes the problem. However, jsbin puts all the code from the JavaScript pane at the bottom of your body. You need the function to be declared before the var tape = new glue(input, output, 60); line, since that line is calling the function. You can tell jsbin where to put the code from the JS pane, by putting %code% where you want it in in the HTML pane. So, if you put a line like <script>%code%</script> before your existing script block, it should fix that. This is totally just a quirk of using jsbin, and won't apply to code you have running in a standalone website (although, even on your standalone site, you need to make sure that the code declaring the glue function comes before the code calling it).
Now, that gets rid of all the errors that are being thrown, but still, the code isn't actually DOING anything. This is because near the beginning of the constructor you have:
this.varOneClone = this.varOne;
this.varTwoClone = this.varTwo;
So varOne starts out equal to varOneClone, and varTwo starts out equal to varTwoClone. The only other place you set them is inside the onChange method, but you only call onChange if varOne != varOneClone or varTwo != varTwoClone. It's like you're saying "Make these 2 values the same, then if they're different, call onChange." Obviously, in that case, onChange is never going to be called.
I realize that it's possible you have more code than you've included here, that IS changing those properties, but I think your goal is to check if the text WITHIN varOne or varTwo has changed, and if so, update the other one, rather than checking if the elements themselves have changed. Since the text can be changed by the user (at least for varOne, since that's an input), it can be changed outside of code. If assumption is correct, you need something like this:
function glue(varOne, varTwo, interval = 60) {
this.varOne = varOne;
this.varTwo = varTwo;
this.varOneCurrentText = this.varOne.value;
this.varTwoCurrentText = this.varTwo.textContent;
this.interval = interval;
this.onChange = function(changedVar) {
if (changedVar == this.varOne) {
this.varTwo.textContent = this.varOneCurrentText = this.varTwoCurrentText = this.varOne.value;
} else if (changedVar == this.varTwo) {
this.varOne.value = this.varOneCurrentText = this.varTwoCurrentText = this.varTwo.textContent;
}
};
this.intervalID = setInterval(function() {
if (this.varOne.value != this.varTwo.textContent) {
if (this.varOne.value != this.varOneCurrentText) {
this.onChange(this.varOne);
} else if (this.varTwo.textContent != this.varTwoCurrentText) {
this.onChange(this.varTwo);
}
}
}.bind(this), this.interval);
//...
}
A couple things to note about this. First, notice that I'm using .value for varOne but .textContent for varTwo. This is because you're passing in a form element (an input) for varOne and a non-form element (a paragraph) for varTwo. These types of elements have different ways of getting their current text. If you can design it so that only form elements will ever be passed in, it will make things easier. But if not, since you probably won't know in advance what type of elements are passed in, you'd need to add a check at the beginning, so you can use the correct property.
Also, while this should work, it would really be better to use events rather than having a continuous loop in the setInterval looking to see if the text has changed. The input has a change event that will be fired anytime its value is changed. You could just update varTwo whenever that event is fired. I don't think there's a similar event for the paragraph element, but you could create a custom event for it. I'm assuming that you're planning on having some other code that will update the text inside the paragraph, since that's not something the user can do directly. If so, then you could fire your custom event at the same time that you update its text. Then when that event is fired, you could update varOne.
I just noticed a typo in a line you have, as well:
var output = document.getElementById("ouput");
The element ID should of course be "output".