dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.07k stars 1.56k forks source link

Implement symbols for private names. #17526

Open karlklose opened 10 years ago

karlklose commented 10 years ago

This feature requires a reference to the library (perhaps by its URI), which is not supported by the internal core implementation of Symbol.

We need support for literal symbols and symbols in the mirror system.

floitschG commented 10 years ago

Issue #19981 has been merged into this issue.

nex3 commented 6 years ago

This would be really useful for determining whether optional arguments were passed in a fully-encapsulated way. When writing wrapper functions like test's expectAsync(), it's important to be able to tell which arguments were actually passed so that we can pass the same number to the inner function. Right now, we either have to use a default value like const Object() that can be spoofed by an external user, or define a private class and use that:

class _Placeholder {
  const _Placeholder();
}
const _placeholder = const _Placeholder();

wrapperFunction([a1 = _placeholder, a2 = _placeholder/*, ...*/]) {
  // ...
}

It would be much nicer to just be able to write

wrapperFunction([a1 = #_placeholder, a2 = #_placeholder/*, ...*/]) {
  // ...
}

but dart2js support is blocking that.

lrhn commented 5 years ago

This is still not working, and it was not fixed by moving to the common front-end.

Private symbols have uses apart from the mirror system, so it doesn't make a difference that dart4web doesn't support mirrors any more.

Dart2js doesn't reject programs containing private symbol literals any more, but it fails to make them private. It means that:

class SomeMock {
  int _private(int _);
  noSuchMethod(i) {
    if (i.memberName == #_private) return i.positionalArguments[0] + 1;
    return super.noSuchMethod(i);
  }
}
main() {
  var mock = SomeMock();
  print(mock._private(1));
}

fails to work correctly (it can be invoked from a different library), and other places where a private symbol is used as a private capability are also not private.

joshualitt commented 2 years ago

Mirrors is no longer supported on web.

sigmundch commented 2 years ago

Reopening since this is still relevant outside mirrors. See note from above:

Private symbols have uses apart from the mirror system, so it doesn't make a difference that dart4web doesn't support mirrors any more.

lrhn commented 2 years ago

Example of how private symbols are not respecting library privacy:

// fs.dart
import "fsaux.dart";

const mainSymbol = #_symbol;
const mainConst = Symbol("_symbol");
final mainNonConst = Symbol("_symbol");
final auxNSM = auxGet(NSM());
final mainNSM = (NSM() as dynamic)._symbol;

void main() {
  var symbols = [
    Pair("mainLiteral", mainSymbol),
    Pair("mainConstructor", mainNonConst),
    Pair("mainConstConstructor", mainConst),
    Pair("mainNSM", mainNSM),
    Pair("auxLiteral", auxSymbol),
    Pair("auxConstConstructor", auxConst),
    Pair("auxConstructor", auxNonConst),
    Pair("auxNSM", auxNSM),
  ];

  var ids = <List<Pair>>[];
  outer: for (var p in symbols) {
    for (var list in ids) {
      if (identical(list.first.symbol, p.symbol)) {
        list.add(p);
        continue outer;
      }
    }
    ids.add([p]);
  }
  var eqs = <List<List<Pair>>>[];
  outer: for (var id in ids) {
    for (var eq in eqs) {
      if (eq.first.first.symbol == id.first.symbol) {
        eq.add(id);
        continue outer;
      }
    }
    eqs.add([id]);
  }
  print(eqs.join("\n"));
}

class NSM {
  dynamic noSuchMethod(i) => i.memberName;
}

class Pair {
  final String name;
  final Symbol symbol;
  Pair(this.name, this.symbol);
  int get hashCode => symbol.hashCode;
  bool operator==(other) => other is Pair && identical(symbol, other.symbol);
  String toString() => name;
}

and

// fsaux.dart
Symbol auxGet(dynamic nsm) => nsm._symbol;
const auxSymbol = #_symbol;
const auxConst = Symbol("_symbol");
final auxNonConst = Symbol("_symbol");

This prints lines consisting of equal symbols (by with their symbolic name), grouped into lists of identical symbols. The VM prints:

[[mainLiteral], [mainNSM]]
[[mainConstructor], [mainConstConstructor, auxConstConstructor], [auxConstructor]]
[[auxLiteral], [auxNSM]]

This means that #_symbol and a NoSuchMethod catured ._symbol inside the main fs.dart library are equal (but not identical) and the same inside the fsaux.dart library, and Symbol("_symbol"), whether const or not, is a different (not private) symbol, which is canonicalized when const. That's correct behavior.

Dart2js prints:

[[mainLiteral, mainConstConstructor, auxLiteral, auxConstConstructor], [mainConstructor], [mainNSM], [auxConstructor], [auxNSM]]

Those are all in one line, so all of them are equal (==). The literals and const constructor invoked ones are identical, even across libraries, the rest are not. That's wrong, and shows that dart2js doesn't implement private symbols at all.