-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Empty array causes "Object not extensible" #700
Comments
Besides providing a means of wire presence, this also reduces object initialization cost because everything not present just falls back to the prototype. I know that this behavior isn't ideal, but unfortunately I don't see an ideal alternative either, so I decided to go with the most performant one. Internally, the library uses the following pattern to handle this case: if (message.someArray.length) {
message.someArray.push(...);
} else {
message.someArray = [ ... ];
} Earlier, I also tried working around this by implementing a getter on the prototype, that returns and replaces itself with a new empty array when invoked. Something like this: Object.defineProperty(messagePrototype, "someArray", {
get: function() {
return this.someArray = [];
},
set: function(value) {
delete this.someArray;
this.someArray = value;
}
}); but I wasn't sure about the performance impact this might have (i.e. regarding hidden class optimizations), so I didn't stick to it. If you have a better idea, let me know! |
I see, maybe we should provide a way for the user to choose, like it's done with option js_default_values = true; // default to false When this value is to true the compiler would set the default values even if the wire is not present, and use this kind of code to detect wire presence. const checkWire = (message, prop) =>
message.hasOwnProperty(prop) &&
(!Array.isArray(message[prop]) || message[prop].length !== 0)
checkWire({a: []}, 'a') // false
checkWire({a: ['b']}, 'a') // true
checkWire({a: 'b'}, 'a') // true
checkWire({}, 'a') // false This behavior should be applied to I'd be glad to work on a PR if you think it may be a good solution. |
For |
Yes I know but this doesn't apply to |
For reference, this'd be a possible utility function to achieve it: function populateArraysAndObjects(type, instance) {
for (var i = 0; i < /* initializes */ type.fieldsArray.length; ++i) {
var field = type._fieldsArray[i];
if (instance.hasOwnProperty(field.name))
return;
if (field.map)
instance[field.name] = {};
else if (field.repeated)
instance[field.name] = [];
};
} While that's quite simple and solely reflection based, it becomes a bit more complicated when codegen comes into play. |
I use It only supports non-packed repeated fields. I will update it soon to behave mostly like If you think this could be integrated here I'll open a PR. |
Like |
That's great! I rebased my fork and used your commented out code only when |
Found a way to integrate this without significantly breaking the API / any test cases. Let me know if this solves your issue. |
Thank you, this is exactly what I was looking for, all my tests passes now. Are you publishing nightly/dev packages to npm and if not when will 6.7 be released? |
protobuf.js version: 6.0.0
When decoding messages with empty arrays or initializing a blank message with
new()
, all arrays are non extensible and inherited from the builder prototype.It makes sense to freeze those array on the prototype, but when using
new()
ordecode()
we should have a working object, not frozen.Can't we have usable objects without duplicating with
Builder.from(message.toJSON())
and settingutil.toJSONOptions.defaults
to true?Basically this shouldn't throw any error:
Replace
{testList: []}
with{testList: ['something']}
and everything works back again.I made a small repro repo to demonstrate what's going on.
Just run
make
and it should install everything and launch mocha. The first three tests should pass, the last two should fail.The text was updated successfully, but these errors were encountered: