Page MenuHomePhabricator

Remove reliance on class static properties that are reserved in JavaScript
Closed, ResolvedPublic

Description

See also: T89721: Static methods can't know which class they belong to or which class they're called with

In order to migrate from our .static container to native properties on the function object. The most common one is name, like in OO.ui.Dialog.static.name.

Here is a complete list of reserved static property names (most of which we are not using, so that's good).

Unsafe:

  • Function (used to interact with class and constructor)
    • fn.constructor
    • fn.prototype
    • fn.length
    • fn.name
    • fn.toString

Safe (can be overridden as own properties, where needed methods can and should never be called on foreign objects anyway)

  • Object
    • toLocaleString
    • valueOf
    • hasOwnProperty
    • isPrototypeOf
    • propertyIsEnumerable
  • Function
    • fn.apply
    • fn.call
    • fn.bind
    • fn.arity (obsolete, safe now)
    • fn.displayName (obsolete, safe now)
    • fn.toSource (old Firefox, safe now)
    • fn.isGenerator (old Firefox, safe now)
    • fn.arguments (obsolete, safe now)
    • fn.caller (obsolete, safe now)

Event Timeline

Krinkle claimed this task.
Krinkle removed Krinkle as the assignee of this task.
Krinkle raised the priority of this task from to Medium.
Krinkle updated the task description. (Show Details)
Krinkle edited projects, added OOUI, VisualEditor; removed Patch-For-Review.
Krinkle set Security to None.
Krinkle added subscribers: cscott, gerritbot, Krinkle and 3 others.
Krinkle removed a subscriber: gerritbot.

I've searched for all the above mentioned property names with prefix static., using GitHub Code Search on wikimedia/*.

The only match that affects JavaScript (either through JavaScript files or PHP code generating JavaScript) is static.name.

  • OO.Factory.
  • OO.ui.ToolFactory (OO.ui.Tool and OO.ui.ToolGroup subclasses).
  • OO.ui.WindowManager (OO.ui.Dialog subclasses).
  • ve.ce.NodeFactory (ve.ce.Node subclasses).
  • ve.dm.ModelRegistry (ve.dm.Model subclasses).
  • ve.ui.ActionFactory (ve.ui.Action subclasses).
  • ve.ui.ToolFactory (ve.ui.Tool subclasses).
  • ve.ui.DataTransferHandlerFactory (ve.ui.DataTransferHandler subclasses).
  • ve.ui.Command

Most of these should be changed to centrally share retrieval of the name. This'll make it easy to add support for a new property name in a future, because we'll be deprecating name.

While most are related to factories, some are not. We should consider having their new names not be the same.

TODO:

  • Some of these classes are missing a default null declaration and @abstract documentation for their static.name property. Thus making it not obvious that subclasses need to implement it.
  • Our factory/registry classes should document what class they expect to be given.
  • Our entity subclasses should document what registry they're generally used with.
Krinkle updated the task description. (Show Details)

It appears the only violation of this is static.name as used through OO.Factory, and their many downstreams:

  • OO.ui.Dialog,
  • ve.ui.Tool,
  • ve.ce.Annotation,
  • ve.Node,
  • (possibly others).

Given these are all closely related and mostly within the same team (Editing team) for now I propose we handle this within this task rather than spawn off further. The only work that needs to be tracked here, I think, is the deciding of the new name, adding and releasing support for it in OOjs, and then ensuring it works as expected in at least one instance of each of these downstreams, all which I'm happy to do — if I can count on review and quality assurance via Editing team (and naturally to feel free to hold off on deploying any changes when there isn't time, this has no rush).

First and foremost, then, is deciding on a name. Below are some proposals with a code example of how they would be used.

Status quo: name

Plain
function MyClass(num) { this.num = num; }
OO.initClass( MyClass );

// Legacy
MyClass.static.name = 'mine';

// Native
MyClass.name = 'mine';

// Using static class syntax (beware, new in ES2022!)
class MyClass() {
  static name = 'mine';
}

var factory = new OO.Factory();
factory.register( MyClass );
factory.create( 'mine', 42 ) instanceof MyClass;
Dialog
OO.ui.MessageDialog = function MessageDialog() {
}
OO.inheritClass( OO.ui.MessageDialog, OO.ui.Dialog );

// Symbolic name for OO.Factory, via OO.ui.WindowManager.
OO.ui.MessageDialog.static.name = 'message';

/**
 * Windows are added by object reference, under either their default name or an explicitly defined name.
 */
OO.ui.WindowManager.prototype.addWindows = function ( windows ) { /* … */ };

OO.ui.windowManager.addWindows( [ new OO.ui.MessageDialog() ] );
OO.ui.windowManager.openWindow( 'message' );

OO.ui.windowManager.addWindows( { myDialog: new OO.ui.MessageDialog() } );
OO.ui.windowManager.openWindow( 'myDialog' );

Option 1: id

Codesearch: No conflicts

MyClass.id = 'mine';

class MyClass() {
  static id = 'mine';
}

/** Class ID for OO.Factory, via OO.ui.WindowManager. */
OO.ui.MessageDialog.id = 'message';

/** Windows are added by object reference, under either their default ID or an explicitly defined ID. */
OO.ui.WindowManager.prototype.addWindows = function ( windows ) { /* … */ };

Option 2: key

Codesearch: No conflicts

MyClass.key = 'mine';

class MyClass() {
  static key = 'mine';
}

/** Class key for OO.Factory, via OO.ui.WindowManager. */
OO.ui.MessageDialog.key = 'message';

/** Windows are added by object reference, under the default class key or an explicitly defined key. */
OO.ui.WindowManager.prototype.addWindows = function ( windows ) { /* … */ };

I have a slight preference for key over ID, but happy with either.

Change 789899 had a related patch set uploaded (by Krinkle; author: Krinkle):

[oojs/core@master] Factory: Add support for registering by `Class.key`

https://gerrit.wikimedia.org/r/789899

Change 789900 had a related patch set uploaded (by Krinkle; author: Krinkle):

[oojs/core@master] Factory: Support registering and creating objects from ES6 classes

https://gerrit.wikimedia.org/r/789900

Change 789899 merged by jenkins-bot:

[oojs/core@master] Factory: Add support for registering by `Class.key`

https://gerrit.wikimedia.org/r/789899

Change 789900 merged by jenkins-bot:

[oojs/core@master] Factory: Support registering and creating objects from ES6 classes

https://gerrit.wikimedia.org/r/789900

Krinkle lowered the priority of this task from Medium to Low.Sep 19 2022, 5:56 PM
Krinkle closed this task as Resolved.EditedJan 19 2023, 7:29 AM
Krinkle removed a project: Patch-For-Review.

Calling this done. OOjs no longer requires use of the reserved name property. Downstream OOUI and VE can adopt .key as they see fit. Adoption in e.g. OO.ui.Dialog can be tracked in one of the three layers of parent tasks instead.

Krinkle renamed this task from Deprecate class static properties that are reserved in JavaScript to Remove reliance on class static properties that are reserved in JavaScript.Jan 19 2023, 7:31 AM