From e5ba894afc319b856e4e9ca9f9171db7fd9223a7 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Thu, 22 Aug 2024 22:23:31 -0400 Subject: [PATCH 1/2] Prevent polluting prototypes with enumerable props Fixes https://github.com/TranscryptOrg/Transcrypt/issues/816 Assigning functions directly to the prototypes of Number, Array, String, etc., causes them to be enumerable, meaning they can show up in "for .. in .." loops and cause unexpected behavior! Instead we can use `Object.defineProperty` (which we already have a helper for called `__setproperty__`. With this method, i) new props are non-enumerable by default, and ii) we can avoid unnecessarily reassigning the same methods, in case there are multiple instances of Transcrypt running. --- .../modules/org/transcrypt/__builtin__.js | 238 ++++++++++-------- 1 file changed, 134 insertions(+), 104 deletions(-) diff --git a/transcrypt/modules/org/transcrypt/__builtin__.js b/transcrypt/modules/org/transcrypt/__builtin__.js index 76619e8c..0eab85ea 100644 --- a/transcrypt/modules/org/transcrypt/__builtin__.js +++ b/transcrypt/modules/org/transcrypt/__builtin__.js @@ -295,7 +295,7 @@ int.__name__ = 'int'; int.__bases__ = [object]; __pragma__ ('ifdef', '__sform__') -Number.prototype.__format__ = function (fmt_spec) { +function _number__format__ (fmt_spec) { if (fmt_spec == undefined || fmt_spec.strip ().length == 0) { return this.toString (); } @@ -534,6 +534,7 @@ Number.prototype.__format__ = function (fmt_spec) { } return val; }; +__setproperty__ (Number.prototype, '__format__', { value: _number__format__ }); __pragma__ ('endif') export function bool (any) { // Always truly returns a bool, rather than something truthy or falsy @@ -1013,13 +1014,14 @@ export function list (iterable) { // All su // Sort is the normal JavaScript sort, Python sort is a non-member function return instance; } -Array.prototype.__class__ = list; // All arrays are lists (not only if constructed by the list ctor), unless constructed otherwise list.__name__ = 'list'; list.__bases__ = [object]; -Array.prototype.__iter__ = function () {return new __PyIterator__ (this);}; +const _listmethods = {}; -Array.prototype.__getslice__ = function (start, stop, step) { +_listmethods.__iter__ = function () {return new __PyIterator__ (this);}; + +_listmethods.__getslice__ = function (start, stop, step) { if (start < 0) { start = this.length + start; } @@ -1046,7 +1048,7 @@ Array.prototype.__getslice__ = function (start, stop, step) { return result; }; -Array.prototype.__setslice__ = function (start, stop, step, source) { +_listmethods.__setslice__ = function (start, stop, step, source) { if (start < 0) { start = this.length + start; } @@ -1069,7 +1071,7 @@ Array.prototype.__setslice__ = function (start, stop, step, source) { } }; -Array.prototype.__repr__ = function () { +_listmethods.__repr__ = function () { if (this.__class__ == set && !this.length) { return 'set()'; } @@ -1091,25 +1093,25 @@ Array.prototype.__repr__ = function () { return result; }; -Array.prototype.__str__ = Array.prototype.__repr__; +_listmethods.__str__ = _listmethods.__repr__; -Array.prototype.append = function (element) { +_listmethods.append = function (element) { this.push (element); }; -Array.prototype.py_clear = function () { +_listmethods.py_clear = function () { this.length = 0; }; -Array.prototype.extend = function (aList) { +_listmethods.extend = function (aList) { this.push.apply (this, aList); }; -Array.prototype.insert = function (index, element) { +_listmethods.insert = function (index, element) { this.splice (index, 0, element); }; -Array.prototype.remove = function (element) { +_listmethods.remove = function (element) { let index = this.indexOf (element); if (index == -1) { throw ValueError ("list.remove(x): x not in list", new Error ()); @@ -1117,11 +1119,11 @@ Array.prototype.remove = function (element) { this.splice (index, 1); }; -Array.prototype.index = function (element) { +_listmethods.index = function (element) { return this.indexOf (element); }; -Array.prototype.py_pop = function (index) { +_listmethods.py_pop = function (index) { if (index == undefined) { return this.pop (); // Remove last element } @@ -1130,18 +1132,18 @@ Array.prototype.py_pop = function (index) { } }; -Array.prototype.py_sort = function () { +_listmethods.py_sort = function () { __sort__.apply (null, [this].concat ([] .slice.apply (arguments))); // Can't work directly with arguments // Python params: (iterable, key = None, reverse = False) // py_sort is called with the Transcrypt kwargs mechanism, and just passes the params on to __sort__ // __sort__ is def'ed with the Transcrypt kwargs mechanism }; -Array.prototype.__add__ = function (aList) { +_listmethods.__add__ = function (aList) { return list (this.concat (aList)); }; -Array.prototype.__mul__ = function (scalar) { +_listmethods.__mul__ = function (scalar) { let result = this; for (let i = 1; i < scalar; i++) { result = result.concat (this); @@ -1149,13 +1151,22 @@ Array.prototype.__mul__ = function (scalar) { return result; }; -Array.prototype.__rmul__ = Array.prototype.__mul__; +_listmethods.__rmul__ = _listmethods.__mul__; + +// All Arrays are lists, whether they were created with list() or not +__setproperty__ (Array.prototype, '__class__', { value: list, writable: true }); +// Assign 'list' methods to Array.prototype +for (let [key, value] of Object.entries (_listmethods)) { + __setproperty__ (Array.prototype, key, { value }); +} // Tuple extensions to Array export function tuple (iterable) { let instance = iterable ? [] .slice.apply (iterable) : []; - instance.__class__ = tuple; // Not all arrays are tuples + // Not all Arrays are tuples, so we set __class__ here on the instance + // (not on Array.prototype as we did with list) + __setproperty__ (instance, '__class__', { value: tuple, writable: true }); return instance; } tuple.__name__ = 'tuple'; @@ -1171,13 +1182,17 @@ export function set (iterable) { instance.add (iterable [index]); } } - instance.__class__ = set; // Not all arrays are sets + // Not all arrays are sets, so we set __class__ here on the instance + // (not on Array.prototype as we did with list) + __setproperty__ (instance, '__class__', { value: set, writable: true }); return instance; } set.__name__ = 'set'; set.__bases__ = [object]; -Array.prototype.__bindexOf__ = function (element) { // Used to turn O (n^2) into O (n log n) +const _setmethods = {}; + +_setmethods.__bindexOf__ = function (element) { // Used to turn O (n^2) into O (n log n) // Since sorting is lex, compare has to be lex. This also allows for mixed lists element += ''; @@ -1203,20 +1218,20 @@ Array.prototype.__bindexOf__ = function (element) { // Used to turn O (n^2) into return -1; }; -Array.prototype.add = function (element) { +_setmethods.add = function (element) { if (this.indexOf (element) == -1) { // Avoid duplicates in set this.push (element); } }; -Array.prototype.discard = function (element) { +_setmethods.discard = function (element) { var index = this.indexOf (element); if (index != -1) { this.splice (index, 1); } }; -Array.prototype.isdisjoint = function (other) { +_setmethods.isdisjoint = function (other) { this.sort (); for (let i = 0; i < other.length; i++) { if (this.__bindexOf__ (other [i]) != -1) { @@ -1226,7 +1241,7 @@ Array.prototype.isdisjoint = function (other) { return true; }; -Array.prototype.issuperset = function (other) { +_setmethods.issuperset = function (other) { this.sort (); for (let i = 0; i < other.length; i++) { if (this.__bindexOf__ (other [i]) == -1) { @@ -1236,11 +1251,11 @@ Array.prototype.issuperset = function (other) { return true; }; -Array.prototype.issubset = function (other) { +_setmethods.issubset = function (other) { return set (other.slice ()) .issuperset (this); // Sort copy of 'other', not 'other' itself, since it may be an ordered sequence }; -Array.prototype.union = function (other) { +_setmethods.union = function (other) { let result = set (this.slice () .sort ()); for (let i = 0; i < other.length; i++) { if (result.__bindexOf__ (other [i]) == -1) { @@ -1250,7 +1265,7 @@ Array.prototype.union = function (other) { return result; }; -Array.prototype.intersection = function (other) { +_setmethods.intersection = function (other) { this.sort (); let result = set (); for (let i = 0; i < other.length; i++) { @@ -1261,7 +1276,7 @@ Array.prototype.intersection = function (other) { return result; }; -Array.prototype.difference = function (other) { +_setmethods.difference = function (other) { let sother = set (other.slice () .sort ()); let result = set (); for (let i = 0; i < this.length; i++) { @@ -1272,11 +1287,11 @@ Array.prototype.difference = function (other) { return result; }; -Array.prototype.symmetric_difference = function (other) { +_setmethods.symmetric_difference = function (other) { return this.union (other) .difference (this.intersection (other)); }; -Array.prototype.py_update = function () { // O (n) +_setmethods.py_update = function () { // O (n) let updated = [] .concat.apply (this.slice (), arguments) .sort (); this.py_clear (); for (let i = 0; i < updated.length; i++) { @@ -1286,7 +1301,7 @@ Array.prototype.py_update = function () { // O (n) } }; -Array.prototype.__eq__ = function (other) { // Also used for list +_setmethods.__eq__ = function (other) { // Also used for list if (this.length != other.length) { return false; } @@ -1302,11 +1317,11 @@ Array.prototype.__eq__ = function (other) { // Also used for list return true; }; -Array.prototype.__ne__ = function (other) { // Also used for list +_setmethods.__ne__ = function (other) { // Also used for list return !this.__eq__ (other); }; -Array.prototype.__le__ = function (other) { +_setmethods.__le__ = function (other) { if (this.__class__ == set) { return this.issubset (other); } @@ -1323,7 +1338,7 @@ Array.prototype.__le__ = function (other) { } }; -Array.prototype.__ge__ = function (other) { +_setmethods.__ge__ = function (other) { if (this.__class__ == set) { return this.issuperset (other); } @@ -1340,7 +1355,7 @@ Array.prototype.__ge__ = function (other) { } }; -Array.prototype.__lt__ = function (other) { +_setmethods.__lt__ = function (other) { return ( this.__class__ == set ? this.issubset (other) && !this.issuperset (other) : @@ -1348,7 +1363,7 @@ Array.prototype.__lt__ = function (other) { ); }; -Array.prototype.__gt__ = function (other) { +_setmethods.__gt__ = function (other) { return ( this.__class__ == set ? this.issuperset (other) && !this.issubset (other) : @@ -1356,6 +1371,11 @@ Array.prototype.__gt__ = function (other) { ); }; +// Assign 'set' methods to Array.prototype +for (let [key, value] of Object.entries (_setmethods)) { + __setproperty__ (Array.prototype, key, { value }); +} + // Byte array extensions export function bytearray (bytable, encoding) { @@ -1385,15 +1405,16 @@ export function bytearray (bytable, encoding) { export var bytes = bytearray; +const _bytearraymethods = {}; -Uint8Array.prototype.__add__ = function (aBytes) { +_bytearraymethods.__add__ = function (aBytes) { let result = new Uint8Array (this.length + aBytes.length); result.set (this); result.set (aBytes, this.length); return result; }; -Uint8Array.prototype.__mul__ = function (scalar) { +_bytearraymethods.__mul__ = function (scalar) { let result = new Uint8Array (scalar * this.length); for (let i = 0; i < scalar; i++) { result.set (this, i * this.length); @@ -1401,7 +1422,12 @@ Uint8Array.prototype.__mul__ = function (scalar) { return result; }; -Uint8Array.prototype.__rmul__ = Uint8Array.prototype.__mul__; +_bytearraymethods.__rmul__ = _bytearraymethods.__mul__; + +// Assign 'bytearray' methods to Uint8Array.prototype +for (let [key, value] of Object.entries (_bytearraymethods)) { + __setproperty__ (Uint8Array.prototype, key, { value }); +} // String extensions @@ -1423,25 +1449,26 @@ export function str (stringable) { } }; -String.prototype.__class__ = str; // All strings are str str.__name__ = 'str'; str.__bases__ = [object]; -String.prototype.__iter__ = function () {new __PyIterator__ (this);}; +const _strmethods = {}; + +_strmethods.__iter__ = function () {new __PyIterator__ (this);}; -String.prototype.__repr__ = function () { +_strmethods.__repr__ = function () { return (this.indexOf ('\'') == -1 ? '\'' + this + '\'' : '"' + this + '"') .py_replace ('\t', '\\t') .py_replace ('\n', '\\n'); }; -String.prototype.__str__ = function () { +_strmethods.__str__ = function () { return this; }; -String.prototype.capitalize = function () { +_strmethods.capitalize = function () { return this.charAt (0).toUpperCase () + this.slice (1); }; -String.prototype.endswith = function (suffix, start=0, end) { +_strmethods.endswith = function (suffix, start=0, end) { if (end === undefined) {end = this.length} const str = this.slice(start, end) @@ -1455,11 +1482,11 @@ String.prototype.endswith = function (suffix, start=0, end) { return false; }; -String.prototype.find = function (sub, start) { +_strmethods.find = function (sub, start) { return this.indexOf (sub, start); }; -String.prototype.__getslice__ = function (start, stop, step) { +_strmethods.__getslice__ = function (start, stop, step) { if (start < 0) { start = this.length + start; } @@ -1484,7 +1511,7 @@ String.prototype.__getslice__ = function (start, stop, step) { }; __pragma__ ('ifdef', '__sform__') -String.prototype.__format__ = function (fmt_spec) { +_strmethods.__format__ = function (fmt_spec) { if (fmt_spec == undefined || fmt_spec.strip ().length == 0) { return this.valueOf (); } @@ -1631,52 +1658,51 @@ __pragma__ ('else') } }); __pragma__ ('endif') - });}, - enumerable: true + });} }); -String.prototype.isalnum = function () { +_strmethods.isalnum = function () { return /^[0-9a-zA-Z]{1,}$/.test(this) } -String.prototype.isalpha = function () { +_strmethods.isalpha = function () { return /^[a-zA-Z]{1,}$/.test(this) } -String.prototype.isdecimal = function () { +_strmethods.isdecimal = function () { return /^[0-9]{1,}$/.test(this) } -String.prototype.isdigit = function () { +_strmethods.isdigit = function () { return this.isdecimal() } -String.prototype.islower = function () { +_strmethods.islower = function () { return /^[a-z]{1,}$/.test(this) } -String.prototype.isupper = function () { +_strmethods.isupper = function () { return /^[A-Z]{1,}$/.test(this) } -String.prototype.isspace = function () { +_strmethods.isspace = function () { return /^[\s]{1,}$/.test(this) } -String.prototype.isnumeric = function () { +_strmethods.isnumeric = function () { return !isNaN (parseFloat (this)) && isFinite (this); }; -String.prototype.join = function (strings) { +_strmethods.join = function (strings) { strings = Array.from (strings); // Much faster than iterating through strings char by char return strings.join (this); }; -String.prototype.lower = function () { +_strmethods.lower = function () { return this.toLowerCase (); }; -String.prototype.py_replace = function (old, aNew, maxreplace) { +_strmethods.py_replace = function (old, aNew, maxreplace) { if (maxreplace === undefined || maxreplace < 0) { return this.split(old).join(aNew); } else if (maxreplace === 0) { @@ -1688,15 +1714,15 @@ String.prototype.py_replace = function (old, aNew, maxreplace) { } }; -String.prototype.lstrip = function () { +_strmethods.lstrip = function () { return this.replace (/^\s*/g, ''); }; -String.prototype.rfind = function (sub, start) { +_strmethods.rfind = function (sub, start) { return this.lastIndexOf (sub, start); }; -String.prototype.rsplit = function (sep, maxsplit) { // Combination of general whitespace sep and positive maxsplit neither supported nor checked, expensive and rare +_strmethods.rsplit = function (sep, maxsplit) { // Combination of general whitespace sep and positive maxsplit neither supported nor checked, expensive and rare if (sep == undefined || sep == null) { sep = /\s+/; var stripped = this.strip (); @@ -1720,11 +1746,11 @@ String.prototype.rsplit = function (sep, maxsplit) { // Combination of genera } }; -String.prototype.rstrip = function () { +_strmethods.rstrip = function () { return this.replace (/\s*$/g, ''); }; -String.prototype.py_split = function (sep, maxsplit) { // Combination of general whitespace sep and positive maxsplit neither supported nor checked, expensive and rare +_strmethods.py_split = function (sep, maxsplit) { // Combination of general whitespace sep and positive maxsplit neither supported nor checked, expensive and rare if (sep == undefined || sep == null) { sep = /\s+/; var stripped = this.strip (); @@ -1747,7 +1773,7 @@ String.prototype.py_split = function (sep, maxsplit) { // Combination of genera } }; -String.prototype.startswith = function (prefix, start=0, end) { +_strmethods.startswith = function (prefix, start=0, end) { if (end === undefined) {end = this.length} const str = this.slice(start, end) @@ -1762,15 +1788,15 @@ String.prototype.startswith = function (prefix, start=0, end) { return false; } -String.prototype.strip = function () { +_strmethods.strip = function () { return this.trim (); }; -String.prototype.upper = function () { +_strmethods.upper = function () { return this.toUpperCase (); }; -String.prototype.__mul__ = function (scalar) { +_strmethods.__mul__ = function (scalar) { var result = ''; for (var i = 0; i < scalar; i++) { result = result + this; @@ -1778,15 +1804,24 @@ String.prototype.__mul__ = function (scalar) { return result; }; -String.prototype.__rmul__ = String.prototype.__mul__; +_strmethods.__rmul__ = _strmethods.__mul__; + +// All Strings are strs, whether they were created with str() or not +__setproperty__ (String.prototype, '__class__', { value: str, writable: true }); +// Assign 'str' methods to String.prototype +for (let [key, value] of Object.entries (_strmethods)) { + __setproperty__ (String.prototype, key, { value }); +} // Dict extensions to object -function __contains__ (element) { +const _dictmethods = {}; + +_dictmethods.__contains__ = function (element) { return this.hasOwnProperty (element); } -function __keys__ () { +_dictmethods.py_keys = function () { var keys = []; for (var attrib in this) { if (!__specialattrib__ (attrib)) { @@ -1796,7 +1831,15 @@ function __keys__ () { return keys; } -function __items__ () { +_dictmethods.__iter__ = function () { + return new __PyIterator__ (this.py_keys ()); +} + +_dictmethods[Symbol.iterator] = function () { + return new __JsIterator__ (this.py_keys ()); +} + +_dictmethods.py_items = function () { var items = []; for (var attrib in this) { if (!__specialattrib__ (attrib)) { @@ -1806,17 +1849,17 @@ function __items__ () { return items; } -function __del__ (key) { +_dictmethods.py_del = function (key) { delete this [key]; } -function __clear__ () { +_dictmethods.py_clear = function () { for (var attrib in this) { delete this [attrib]; } } -function __getdefault__ (aKey, aDefault) { // Each Python object already has a function called __get__, so we call this one __getdefault__ +_dictmethods.py_get = function (aKey, aDefault) { var result = this [aKey]; if (result == undefined) { result = this ['py_' + aKey] @@ -1824,7 +1867,7 @@ function __getdefault__ (aKey, aDefault) { // Each Python object already has a return result == undefined ? (aDefault == undefined ? null : aDefault) : result; } -function __setdefault__ (aKey, aDefault) { +_dictmethods.py_setdefault = function (aKey, aDefault) { var result = this [aKey]; if (result != undefined) { return result; @@ -1834,7 +1877,7 @@ function __setdefault__ (aKey, aDefault) { return val; } -function __pop__ (aKey, aDefault) { +_dictmethods.py_pop = function (aKey, aDefault) { var result = this [aKey]; if (result != undefined) { delete this [aKey]; @@ -1848,7 +1891,7 @@ function __pop__ (aKey, aDefault) { return aDefault; } -function __popitem__ () { +_dictmethods.py_popitem = function () { var aKey = Object.keys (this) [0]; if (aKey == null) { throw KeyError ("popitem(): dictionary is empty", new Error ()); @@ -1858,13 +1901,13 @@ function __popitem__ () { return result; } -function __update__ (aDict) { +_dictmethods.py_update = function (aDict) { for (var aKey in aDict) { this [aKey] = aDict [aKey]; } } -function __values__ () { +_dictmethods.py_values = function () { var values = []; for (var attrib in this) { if (!__specialattrib__ (attrib)) { @@ -1872,14 +1915,13 @@ function __values__ () { } } return values; - } -function __dgetitem__ (aKey) { +_dictmethods.__getitem__ = function (aKey) { return this [aKey]; } -function __dsetitem__ (aKey, aValue) { +_dictmethods.__setitem__ = function (aKey, aValue) { this [aKey] = aValue; } @@ -1939,23 +1981,11 @@ export function dict (objectOrPairs) { // Trancrypt interprets e.g. {aKey: 'aValue'} as a Python dict literal rather than a JavaScript object literal // So dict literals rather than bare Object literals will be passed to JavaScript libraries // Some JavaScript libraries call all enumerable callable properties of an object that's passed to them - // So the properties of a dict should be non-enumerable - __setproperty__ (instance, '__class__', {value: dict, enumerable: false, writable: true}); - __setproperty__ (instance, '__contains__', {value: __contains__, enumerable: false}); - __setproperty__ (instance, 'py_keys', {value: __keys__, enumerable: false}); - __setproperty__ (instance, '__iter__', {value: function () {new __PyIterator__ (this.py_keys ());}, enumerable: false}); - __setproperty__ (instance, Symbol.iterator, {value: function () {new __JsIterator__ (this.py_keys ());}, enumerable: false}); - __setproperty__ (instance, 'py_items', {value: __items__, enumerable: false}); - __setproperty__ (instance, 'py_del', {value: __del__, enumerable: false}); - __setproperty__ (instance, 'py_clear', {value: __clear__, enumerable: false}); - __setproperty__ (instance, 'py_get', {value: __getdefault__, enumerable: false}); - __setproperty__ (instance, 'py_setdefault', {value: __setdefault__, enumerable: false}); - __setproperty__ (instance, 'py_pop', {value: __pop__, enumerable: false}); - __setproperty__ (instance, 'py_popitem', {value: __popitem__, enumerable: false}); - __setproperty__ (instance, 'py_update', {value: __update__, enumerable: false}); - __setproperty__ (instance, 'py_values', {value: __values__, enumerable: false}); - __setproperty__ (instance, '__getitem__', {value: __dgetitem__, enumerable: false}); // Needed since compound keys necessarily - __setproperty__ (instance, '__setitem__', {value: __dsetitem__, enumerable: false}); // trigger overloading to deal with slices + // So the properties of a dict should be non-enumerable (default) + __setproperty__ (instance, '__class__', { value: dict, writable: true }); + for (let [key, value] of Object.entries (_dictmethods)) { + __setproperty__ (instance, key, { value }); + } return instance; } @@ -1970,7 +2000,7 @@ function __setdoc__ (docString) { } // Python classes, methods and functions are all translated to JavaScript functions -__setproperty__ (Function.prototype, '__setdoc__', {value: __setdoc__, enumerable: false}); +__setproperty__ (Function.prototype, '__setdoc__', {value: __setdoc__}); // General operator overloading, only the ones that make most sense in matrix and complex operations From 732602baf1b3309ea019c7b32013407c438f9dfc Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Fri, 23 Aug 2024 02:01:14 -0400 Subject: [PATCH 2/2] Put dict methods on the dict.prototype I noted that `list` methods get assigned to `Array.prototype`, `str` methods get assigned to `String.prototype`, etc. `dict` methods do not get assigned to `Object.prototype` (I am guessing this is for a good reason) The current code has the `dict` methods being to each new instance in the constructor. It is tidier to declare them on the `dict` prototype and then create each dict instance from that. This is also more efficient because properties placed on the dict prototype need only be declared once, as opposed to being declared for each dict. --- .../modules/org/transcrypt/__builtin__.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/transcrypt/modules/org/transcrypt/__builtin__.js b/transcrypt/modules/org/transcrypt/__builtin__.js index 0eab85ea..809bec24 100644 --- a/transcrypt/modules/org/transcrypt/__builtin__.js +++ b/transcrypt/modules/org/transcrypt/__builtin__.js @@ -1925,8 +1925,14 @@ _dictmethods.__setitem__ = function (aKey, aValue) { this [aKey] = aValue; } +__setproperty__ (dict.prototype, '__class__', { value: dict, writable: true }); +// Assign 'dict' methods to dict.prototype +for (let [key, value] of Object.entries (_dictmethods)) { + __setproperty__ (dict.prototype, key, { value }); +} + export function dict (objectOrPairs) { - var instance = {}; + var instance = Object.create (dict.prototype); if (!objectOrPairs || objectOrPairs instanceof Array) { // It's undefined or an array of pairs if (objectOrPairs) { for (var index = 0; index < objectOrPairs.length; index++) { @@ -1968,7 +1974,7 @@ export function dict (objectOrPairs) { } } else if (objectOrPairs instanceof Object) { // Passed object is a JavaScript object but not yet a dict, don't copy it - instance = objectOrPairs; + Object.assign (instance, objectOrPairs); } else { // We have already covered Array so this indicates // that the passed object is not a js object - i.e. @@ -1977,15 +1983,6 @@ export function dict (objectOrPairs) { throw ValueError ("Invalid type of object for dict creation", new Error ()); } } - - // Trancrypt interprets e.g. {aKey: 'aValue'} as a Python dict literal rather than a JavaScript object literal - // So dict literals rather than bare Object literals will be passed to JavaScript libraries - // Some JavaScript libraries call all enumerable callable properties of an object that's passed to them - // So the properties of a dict should be non-enumerable (default) - __setproperty__ (instance, '__class__', { value: dict, writable: true }); - for (let [key, value] of Object.entries (_dictmethods)) { - __setproperty__ (instance, key, { value }); - } return instance; }