reflect-metadata icon indicating copy to clipboard operation
reflect-metadata copied to clipboard

Reflect.getMetadata not working on proxified objects

Open nicomouss opened this issue 7 years ago • 8 comments

See the right behaviour of Reflect.get() below, compared to Reflect.getMetadata() when a proxy is involved.

import "reflect-metadata";

const METADATA_KEY = Symbol();

const post = {
    id: 1000
}

Reflect.defineMetadata(METADATA_KEY, "Hello", post);

const postProxied = new Proxy(post, {});

console.log(`${Reflect.get(post, "id")}`); // OK, displays 1000
console.log(`${Reflect.get(postProxied, "id")}`); // OK, displays 1000

console.log(`${Reflect.getMetadata(METADATA_KEY, post)}`); // OK, displays "Hello"
console.log(`${Reflect.getMetadata(METADATA_KEY, postProxied)}`); // NOT OK, undefined metadata

My question then: how do I access an object's metadata when this object has been proxified? Everything shouldn't be transparent like it is when using Reflect.get()?

nicomouss avatar Apr 14 '19 16:04 nicomouss

I’d think the Proxy has to allow you to do that, by having Proxy traps and a target argument to the Reflect method - otherwise you can’t/shouldn’t.

ljharb avatar Apr 14 '19 18:04 ljharb

@ljharb Thanks for your feedback.

Maybe I am missing something here, but I guess everything should be transparent, and the one-to-one match between proxy methods traps and Reflect API should be kept consistent.

If in native Reflect API we have:

Reflect​.apply()
Reflect​.construct()
Reflect​.define​Property()
Reflect​.delete​Property()
Reflect​.get()
Reflect​.get​OwnProperty​Descriptor()
Reflect​.get​PrototypeOf()
Reflect​.has()
Reflect​.isExtensible()
Reflect​.own​Keys()
Reflect​.prevent​Extensions()
Reflect​.set()
Reflect​.set​PrototypeOf()

Then Proxy handler traps allowed are:

handler​.apply()
handler​.construct()
handler​.define​Property()
handler​.delete​Property()
handler​.get()
handler​.get​OwnProperty​Descriptor()
handler​.get​PrototypeOf()
handler​.has()
handler​.isExtensible()
handler​.own​Keys()
handler​.prevent​Extensions()
handler​.set()
handler​.set​PrototypeOf()

Now if reflect-metadata is extending the Reflect API with these methods:

Reflect.defineMetadata();
Reflect.hasMetadata();
Reflect.hasOwnMetadata();
Reflect.getMetadata();
Reflect.getOwnMetadata();
Reflect.getMetadataKeys();
Reflect.getOwnMetadataKeys();
Reflect.deleteMetadata();

Then it should also extend Proxy handler with the following trap methods:

handler.defineMetadata();
handler.hasMetadata();
handler.hasOwnMetadata();
handler.getMetadata();
handler.getOwnMetadata();
handler.getMetadataKeys();
handler.getOwnMetadataKeys();
handler.deleteMetadata();

Then things would be consistent.

As for now and current implementation of reflect-metadata: if at any point in a third-party library some code is proxyfying an object on which I have set up metadata beforehand, then I suddenly loose access to these metadatas ... Whereas the proxy should transparently forward the Reflect.getMetadata() call to the targeted object.

Things are confusing here because in the reflect-metada documentation (https://github.com/rbuckton/reflect-metadata - see Goals section), we have:

Metadata should be available not only on an object but also through a Proxy, with related traps.

Has something been implemented somewhere regarding that? Any example available?

nicomouss avatar Apr 15 '19 04:04 nicomouss

I agree with you; it’s been established many times in committee that nothing is allowed on Reflect that isn’t also a proxy trap.

ljharb avatar Apr 15 '19 04:04 ljharb

@nicomouss did you find a solution other than copying metadata manually? @ljharb according to MDN if a trap has not been defined, the default behavior is to forward the operation to the target. That means that even we don't have Proxy traps for metadata, we still should be able to read it from the original object wrapped in a proxy.

fxlrnrpt avatar Jan 25 '20 17:01 fxlrnrpt

@aigoncharov yes, but if there isn’t a Proxy trap, there isn’t allowed to be a corresponding function on Reflect.

ljharb avatar Jan 25 '20 17:01 ljharb

@rbuckton here's the live demo based on the code provided by @nicomouss

fxlrnrpt avatar Jan 25 '20 17:01 fxlrnrpt

Do you have any news about this issue?

klerick avatar Nov 28 '20 17:11 klerick

We're not planning on any further changes to how reflect-metadata works with respect to proxies. If we allowed metadata to pass through a proxy without the ability to intercept an operation in a proxy handler, there would be no way to prevent private implementation details in metadata from leaking through a proxy/membrane.

We're discussing how metadata should be handled in ECMAScript as part of the decorators proposal. Currently, we have two options under consideration:

  1. Adding a getMetadata proxy trap to allow interception or forwarding of the request, or
  2. Attaching metadata via a Symbol.metadata built-in symbol, which could then be intercepted via existing handlers.

The current implementation utilizes a WeakMap, and WeakMaps also do not automatically flow through proxies. The only approach I can recommend for now is to copy metadata from the target to the proxy.

rbuckton avatar Apr 15 '21 00:04 rbuckton