Ticket #81 (new defect)

Opened 2 years ago

Last modified 9 months ago

Problems with 'use default namespace public'

Reported by: lth Assigned to: graydon
Type: defect Priority: major
Milestone: M1 Component: RefImpl
Version: 4 Keywords:
Cc: graydon,jeffdyer

Description

Consider builtins/!Math.es. If I add use default namespace public inside the !MathInternals package, and remove the public qualifier on the !Math class, then the class is made public. But if I remove the public qualifiers from the members they are still not made public, nor if I add the pragma inside the class.

Attachments

Change History

Changed 1 year ago by lth

  • owner set to graydon

Simpler test case(s):

package R {
  use default namespace public;
  class C {
    var v = 10;
    public var w = 11;
    const c = 20;
    public const d = 21;
    function f() { return 37 }
    public function g() { return 38 }
  }
}
>> x = new R.C
[object C]
>> x.v
>> x.w
11
>> x.c
>> x.d
21
>> x.f()
[stack] []
**ERROR** EvalError: uncaught exception: ReferenceError:
   unresolved object reference {multiname: [ns public '']::f }
  (near builtins/Error.es:83.47-83.47)
>> x.g()
38

Adding another use default namespace public inside C makes no difference.

Changed 1 year ago by graydon

Further wrinkle in this: what's happening is that "public" refers to the public namespace associated with package R here. There is a general problem that the namespace denoted by "public" is not visible or denotable outside a package, and can only be accessed indirectly by opening the package using a wildcard import. This is no good.

There's a message to the reflector detailing it. I'll paste it here.

--snip--

I thought the situation was a little more broken than it seems to be. There was some bug that was surprising me, but it seems to be gone. I think there are only two issues:

  • First, there is no way to denote [public ""] from inside package P, -- aside from switching packages, which is currently not legal in every syntactic construct -- and there is no way to denote [public P] explicitly from inside package Q. You can import P.f to get access to "P.f" as a qualified reference, but this implies the thing you want to see is a global fixture in P. If you want to see some instance variable o.[public P]::f in an instance o, there's no way to denote it from package Q. You have to open [public P] into your open namespace list using "import P.*" and hope for the best. That's suboptimal.
  • Second, if I say "import x = P.x" to get a local name x I can use without qualifying it, the current implementation makes a get/set fixture x in my current package. This means that the import is not lexically scoped to the construct I use it in, and that it's both hoisted out to the global much of the time, and also collides with any other hoisted aliasing imports.

These issue are solvable, I think. For the first, we need to reify [public P] in some place normal code can get at it. Say, stick a fixture in [public ""] for package P in the global frame. In other words, the name [public ""]::P should point directly to the namespace fixture [public P]. Then anyone can denote [public P]::f by saying "P::f", so long as they haven't been silly and shadowed the name P. It's the same as using P.f in a context where P.f is an import, or using ns::f for some dynamic value of ns.

This might only be a partial solution though, since an instance reference through a dot-separated path like "o.P.Q.R::f" will not work, if we want it to mean o.[public P.Q.R]::f. It's am ambiguous parse, grr. Can we permit writing o.(P.Q.R::f)? Gross but ... how else do you denote it?

The second issue is just an implementation bug -- I think -- and we can solve it by making a specific type of block-local alias fixture, not a hoisted get/set pair. In particular: the block introduced by "package { ... }" should actually contain all the aliases within it -- as it contains the effect of opening a package with "import P.*" -- and not let the aliases hoist out along with other global fixtures.

Changed 9 months ago by lth

I've had another problem, in that I explicitly want to talk about the name with no namespace (necessary in the builtins, which want to talk about the prototype names).

For this problem, Jeff and I think that the syntax ::x might be OK (the namespace is absent, which is what we want to express). So, "o.::x" to talk about the x property that has no namespace; this is not a multiname and so different from "o.x".

Note: See TracTickets for help on using tickets.