-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDynamicProto.ts
More file actions
638 lines (549 loc) · 26.5 KB
/
DynamicProto.ts
File metadata and controls
638 lines (549 loc) · 26.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { getGlobal, objCreate, objHasOwnProperty, throwTypeError } from "@nevware21/ts-utils";
interface DynamicGlobalSettings {
/**
* Stores the global options which will also be exposed on the runtime global
*/
o: IDynamicProtoOpts,
/**
* Internal Global used to generate a unique dynamic class name, every new class will increase this value
* @ignore
*/
n: number
};
const UNDEFINED = "undefined";
/**
* Constant string defined to support minimization
* @ignore
*/
const Constructor = 'constructor';
/**
* Constant string defined to support minimization
* @ignore
*/
const Prototype = 'prototype';
/**
* Constant string defined to support minimization
* @ignore
*/
const strFunction = 'function';
/**
* Used to define the name of the instance function lookup table
* @ignore
*/
const DynInstFuncTable = '_dynInstFuncs';
/**
* Name used to tag the dynamic prototype function
* @ignore
*/
const DynProxyTag = '_isDynProxy';
/**
* Name added to a prototype to define the dynamic prototype "class" name used to lookup the function table
* @ignore
*/
const DynClassName = '_dynClass';
/**
* Prefix added to the classname to avoid any name clashes with other instance level properties
* @ignore
*/
const DynClassNamePrefix = '_dynCls$';
/**
* A tag which is used to check if we have already to attempted to set the instance function if one is not present
* @ignore
*/
const DynInstChkTag = '_dynInstChk';
/**
* A tag which is used to check if we are allows to try and set an instance function is one is not present. Using the same
* tag name as the function level but a different const name for readability only.
*/
const DynAllowInstChkTag = DynInstChkTag;
/**
* The global (imported) instances where the global performance options are stored
*/
const DynProtoDefaultOptions = '_dfOpts';
/**
* Value used as the name of a class when it cannot be determined
* @ignore
*/
const UnknownValue = '_unknown_';
/**
* Constant string defined to support minimization
* @ignore
*/
const str__Proto = "__proto__";
/**
* The polyfill version of __proto__ so that it doesn't cause issues for anyone not expecting it to exist
*/
const DynProtoBaseProto = "_dyn" + str__Proto;
/**
* Runtime Global holder for dynamicProto settings
*/
const DynProtoGlobalSettings = "__dynProto$Gbl";
/**
* Track the current prototype for IE8 as you can't look back to get the prototype
*/
const DynProtoCurrent = "_dynInstProto";
/**
* Constant string defined to support minimization
* @ignore
*/
const strUseBaseInst = 'useBaseInst';
/**
* Constant string defined to support minimization
* @ignore
*/
const strSetInstFuncs = 'setInstFuncs';
const Obj = Object;
/**
* Pre-lookup to check if we are running on a modern browser (i.e. not IE8)
* @ignore
*/
let _objGetPrototypeOf = Obj["getPrototypeOf"];
/**
* Pre-lookup to check for the existence of this function
*/
let _objGetOwnProps = Obj["getOwnPropertyNames"];
// Since 1.1.7 moving these to the runtime global to work around mixed version and module issues
// See Issue https://github.com/microsoft/DynamicProto-JS/issues/57 for details
let _gbl = getGlobal();
let _gblInst: DynamicGlobalSettings = _gbl[DynProtoGlobalSettings] || (_gbl[DynProtoGlobalSettings] = {
o: {
[strSetInstFuncs]: true,
[strUseBaseInst]: true
},
n: 1000 // Start new global index @ 1000 so we "fix" some cases when mixed with 1.1.6 or earlier
});
/**
* Helper used to check whether the target is an Object prototype or Array prototype
* @ignore
*/
function _isObjectOrArrayPrototype(target:any) {
return target && (target === Obj[Prototype] || target === Array[Prototype]);
}
/**
* Helper used to check whether the target is an Object prototype, Array prototype or Function prototype
* @ignore
*/
function _isObjectArrayOrFunctionPrototype(target:any) {
return _isObjectOrArrayPrototype(target) || target === Function[Prototype];
}
/**
* Helper used to get the prototype of the target object as getPrototypeOf is not available in an ES3 environment.
* @ignore
*/
function _getObjProto(target:any) {
let newProto;
if (target) {
// This method doesn't exist in older browsers (e.g. IE8)
if (_objGetPrototypeOf) {
return _objGetPrototypeOf(target);
}
let curProto = target[str__Proto] || target[Prototype] || (target[Constructor] ? target[Constructor][Prototype] : null);
// Using the pre-calculated value as IE8 doesn't support looking up the prototype of a prototype and thus fails for more than 1 base class
newProto = target[DynProtoBaseProto] || curProto;
if (!objHasOwnProperty(target, DynProtoBaseProto)) {
// As this prototype doesn't have this property then this is from an inherited class so newProto is the base to return so save it
// so we can look it up value (which for a multiple hierarchy dynamicProto will be the base class)
delete target[DynProtoCurrent]; // Delete any current value allocated to this instance so we pick up the value from prototype hierarchy
newProto = target[DynProtoBaseProto] = target[DynProtoCurrent] || target[DynProtoBaseProto];
target[DynProtoCurrent] = curProto;
}
}
return newProto;
}
/**
* Helper to get the properties of an object, including none enumerable ones as functions on a prototype in ES6
* are not enumerable.
* @param target
*/
function _forEachProp(target: any, func: (name: string) => void) {
let props: string[] = [];
if (_objGetOwnProps) {
props = _objGetOwnProps(target);
} else {
for (let name in target) {
if (typeof name === "string" && objHasOwnProperty(target, name)) {
props.push(name);
}
}
}
if (props && props.length > 0) {
for (let lp = 0; lp < props.length; lp++) {
func(props[lp]);
}
}
}
/**
* Helper function to check whether the provided function name is a potential candidate for dynamic
* callback and prototype generation.
* @param target The target object, may be a prototype or class object
* @param funcName The function name
* @param skipOwn Skips the check for own property
* @ignore
*/
function _isDynamicCandidate(target:any, funcName:string, skipOwn:boolean) {
return (funcName !== Constructor && typeof target[funcName] === strFunction && (skipOwn || objHasOwnProperty(target, funcName)) && funcName !== str__Proto && funcName !== Prototype);
}
/**
* Helper to throw a TypeError exception
* @param message the message
* @ignore
*/
function _throwTypeError(message:string) {
throwTypeError("DynamicProto: " + message);
}
/**
* Returns a collection of the instance functions that are defined directly on the thisTarget object, it does
* not return any inherited functions
* @param thisTarget The object to get the instance functions from
* @ignore
*/
function _getInstanceFuncs(thisTarget:any): any {
// Get the base proto
var instFuncs = objCreate(null);
// Save any existing instance functions
_forEachProp(thisTarget, (name) => {
// Don't include any dynamic prototype instances - as we only want the real functions
if (!instFuncs[name] && _isDynamicCandidate(thisTarget, name, false)) {
// Create an instance callback for passing the base function to the caller
instFuncs[name] = thisTarget[name];
}
});
return instFuncs;
}
/**
* Returns whether the value is included in the array
* @param values The array of values
* @param value The value
*/
function _hasVisited(values:any[], value:any) {
for (let lp = values.length - 1; lp >= 0; lp--) {
if (values[lp] === value) {
return true;
}
}
return false;
}
/**
* Returns an object that contains callback functions for all "base/super" functions, this is used to "save"
* enabling calling super.xxx() functions without requiring that the base "class" has defined a prototype references
* @param target The current instance
* @ignore
*/
function _getBaseFuncs(classProto:any, thisTarget:any, instFuncs:any, useBaseInst:boolean): any {
function _instFuncProxy(target:any, funcHost: any, funcName: string) {
let theFunc = funcHost[funcName];
if (theFunc[DynProxyTag] && useBaseInst) {
// grab and reuse the hosted looking function (if available) otherwise the original passed function
let instFuncTable = target[DynInstFuncTable] || {};
if (instFuncTable[DynAllowInstChkTag] !== false) {
theFunc = (instFuncTable[funcHost[DynClassName]] || {})[funcName] || theFunc;
}
}
return function() {
// eslint-disable-next-line prefer-rest-params
return theFunc.apply(target, arguments);
};
}
// Start creating a new baseFuncs by creating proxies for the instance functions (as they may get replaced)
var baseFuncs = objCreate(null);
_forEachProp(instFuncs, (name) => {
// Create an instance callback for passing the base function to the caller
baseFuncs[name] = _instFuncProxy(thisTarget, instFuncs, name);
});
// Get the base prototype functions
var baseProto = _getObjProto(classProto);
let visited:any[] = [];
// Don't include base object functions for Object, Array or Function
while (baseProto && !_isObjectArrayOrFunctionPrototype(baseProto) && !_hasVisited(visited, baseProto)) {
// look for prototype functions
_forEachProp(baseProto, (name) => {
// Don't include any dynamic prototype instances - as we only want the real functions
// For IE 7/8 the prototype lookup doesn't provide the full chain so we need to bypass the
// hasOwnProperty check we get all of the methods, main difference is that IE7/8 doesn't return
// the Object prototype methods while bypassing the check
if (!baseFuncs[name] && _isDynamicCandidate(baseProto, name, !_objGetPrototypeOf)) {
// Create an instance callback for passing the base function to the caller
baseFuncs[name] = _instFuncProxy(thisTarget, baseProto, name);
}
});
// We need to find all possible functions that might be overloaded by walking the entire prototype chain
// This avoids the caller from needing to check whether it's direct base class implements the function or not
// by walking the entire chain it simplifies the usage and issues from upgrading any of the base classes.
visited.push(baseProto);
baseProto = _getObjProto(baseProto);
}
return baseFuncs;
}
function _getInstFunc(target: any, funcName: string, proto: any, currentDynProtoProxy: any) {
let instFunc = null;
// We need to check whether the class name is defined directly on this prototype otherwise
// it will walk the proto chain and return any parent proto classname.
if (target && objHasOwnProperty(proto, DynClassName)) {
let instFuncTable = target[DynInstFuncTable] || objCreate(null);
instFunc = (instFuncTable[proto[DynClassName]] || objCreate(null))[funcName];
if (!instFunc) {
// Avoid stack overflow from recursive calling the same function
_throwTypeError("Missing [" + funcName + "] " + strFunction);
}
// We have the instance function, lets check it we can speed up further calls
// by adding the instance function back directly on the instance (avoiding the dynamic func lookup)
if (!instFunc[DynInstChkTag] && instFuncTable[DynAllowInstChkTag] !== false) {
// If the instance already has an instance function we can't replace it
let canAddInst = !objHasOwnProperty(target, funcName);
// Get current prototype
let objProto = _getObjProto(target);
let visited:any[] = [];
// Lookup the function starting at the top (instance level prototype) and traverse down, if the first matching function
// if nothing is found or if the first hit is a dynamic proto instance then we can safely add an instance shortcut
while (canAddInst && objProto && !_isObjectArrayOrFunctionPrototype(objProto) && !_hasVisited(visited, objProto)) {
let protoFunc = objProto[funcName];
if (protoFunc) {
canAddInst = (protoFunc === currentDynProtoProxy);
break;
}
// We need to find all possible initial functions to ensure that we don't bypass a valid override function
visited.push(objProto);
objProto = _getObjProto(objProto);
}
try {
if (canAddInst) {
// This instance doesn't have an instance func and the class hierarchy does have a higher level prototype version
// so it's safe to directly assign for any subsequent calls (for better performance)
target[funcName] = instFunc;
}
// Block further attempts to set the instance function for any
instFunc[DynInstChkTag] = 1;
} catch (e) {
// Don't crash if the object is readonly or the runtime doesn't allow changing this
// And set a flag so we don't try again for any function
instFuncTable[DynAllowInstChkTag] = false;
}
}
}
return instFunc;
}
function _getProtoFunc(funcName: string, proto: any, currentDynProtoProxy: any) {
let protoFunc = proto[funcName];
// Check that the prototype function is not a self reference -- try to avoid stack overflow!
if (protoFunc === currentDynProtoProxy) {
// It is so lookup the base prototype
protoFunc = _getObjProto(proto)[funcName];
}
if (typeof protoFunc !== strFunction) {
_throwTypeError("[" + funcName + "] is not a " + strFunction);
}
return protoFunc;
}
/**
* Add the required dynamic prototype methods to the the class prototype
* @param proto - The class prototype
* @param className - The instance classname
* @param target - The target instance
* @param baseInstFuncs - The base instance functions
* @param setInstanceFunc - Flag to allow prototype function to reset the instance function if one does not exist
* @ignore
*/
function _populatePrototype(proto:any, className:string, target:any, baseInstFuncs:any, setInstanceFunc:boolean) {
function _createDynamicPrototype(proto:any, funcName:string) {
let dynProtoProxy = function() {
// Use the instance or prototype function
let instFunc = _getInstFunc(this, funcName, proto, dynProtoProxy) || _getProtoFunc(funcName, proto, dynProtoProxy);
// eslint-disable-next-line prefer-rest-params
return instFunc.apply(this, arguments);
};
// Tag this function as a proxy to support replacing dynamic proxy elements (primary use case is for unit testing
// via which can dynamically replace the prototype function reference)
(dynProtoProxy as any)[DynProxyTag] = 1;
return dynProtoProxy;
}
if (!_isObjectOrArrayPrototype(proto)) {
let instFuncTable = target[DynInstFuncTable] = target[DynInstFuncTable] || objCreate(null);
if (!_isObjectOrArrayPrototype(instFuncTable)) {
let instFuncs = instFuncTable[className] = (instFuncTable[className] || objCreate(null)); // fetch and assign if as it may not exist yet
// Set whether we are allow to lookup instances, if someone has set to false then do not re-enable
if (instFuncTable[DynAllowInstChkTag] !== false) {
instFuncTable[DynAllowInstChkTag] = !!setInstanceFunc;
}
if (!_isObjectOrArrayPrototype(instFuncs)) {
_forEachProp(target, (name) => {
// Only add overridden functions
if (_isDynamicCandidate(target, name, false) && target[name] !== baseInstFuncs[name] ) {
// Save the instance Function to the lookup table and remove it from the instance as it's not a dynamic proto function
instFuncs[name] = target[name];
delete target[name];
// Add a dynamic proto if one doesn't exist or if a prototype function exists and it's not a dynamic one
if (!objHasOwnProperty(proto, name) || (proto[name] && !proto[name][DynProxyTag])) {
proto[name] = _createDynamicPrototype(proto, name);
}
}
});
}
}
}
}
/**
* Checks whether the passed prototype object appears to be correct by walking the prototype hierarchy of the instance
* @param classProto The class prototype instance
* @param thisTarget The current instance that will be checked whether the passed prototype instance is in the hierarchy
* @ignore
*/
function _checkPrototype(classProto:any, thisTarget:any) {
// This method doesn't existing in older browsers (e.g. IE8)
if (_objGetPrototypeOf) {
// As this is primarily a coding time check, don't bother checking if running in IE8 or lower
let visited:any[] = [];
let thisProto = _getObjProto(thisTarget);
while (thisProto && !_isObjectArrayOrFunctionPrototype(thisProto) && !_hasVisited(visited, thisProto)) {
if (thisProto === classProto) {
return true;
}
// This avoids the caller from needing to check whether it's direct base class implements the function or not
// by walking the entire chain it simplifies the usage and issues from upgrading any of the base classes.
visited.push(thisProto);
thisProto = _getObjProto(thisProto);
}
return false;
}
// If objGetPrototypeOf doesn't exist then just assume everything is ok.
return true;
}
/**
* Gets the current prototype name using the ES6 name if available otherwise falling back to a use unknown as the name.
* It's not critical for this to return a name, it's used to decorate the generated unique name for easier debugging only.
* @param target
* @param unknownValue
* @ignore
*/
function _getObjName(target:any, unknownValue?:string) {
if (objHasOwnProperty(target, Prototype)) {
// Look like a prototype
return target.name || unknownValue || UnknownValue
}
return (((target || {})[Constructor]) || {}).name || unknownValue || UnknownValue;
}
/**
* Interface to define additional configuration options to control how the dynamic prototype functions operate.
*/
export interface IDynamicProtoOpts {
/**
* Should the dynamic prototype attempt to set an instance function for instances that do not already have an
* function of the same name or have been extended by a class with a (non-dynamic proto) prototype function.
*/
setInstFuncs: boolean,
/**
* When looking for base (super) functions if it finds a dynamic proto instances can it use the instance functions
* and bypass the prototype lookups. Defaults to true.
*/
useBaseInst?: boolean
}
/**
* The delegate signature for the function used as the callback for dynamicProto()
* @typeparam DPType This is the generic type of the class, used to keep intellisense valid for the proxy instance, even
* though it is only a proxy that only contains the functions
* @param theTarget This is the real "this" of the current target object
* @param baseFuncProxy The is a proxy object which ONLY contains this function that existed on the "this" instance before
* calling dynamicProto, it does NOT contain properties of this. This is basically equivalent to using the "super" keyword.
*/
export type DynamicProtoDelegate<DPType> = (theTarget:DPType, baseFuncProxy?:DPType) => void;
/**
* Helper function when creating dynamic (inline) functions for classes, this helper performs the following tasks :-
* - Saves references to all defined base class functions
* - Calls the delegateFunc with the current target (this) and a base object reference that can be used to call all "super" functions.
* - Will populate the class prototype for all overridden functions to support class extension that call the prototype instance.
* Callers should use this helper when declaring all function within the constructor of a class, as mentioned above the delegateFunc is
* passed both the target "this" and an object that can be used to call any base (super) functions, using this based object in place of
* super.XXX() (which gets expanded to _super.prototype.XXX()) provides a better minification outcome and also ensures the correct "this"
* context is maintained as TypeScript creates incorrect references using super.XXXX() for dynamically defined functions i.e. Functions
* defined in the constructor or some other function (rather than declared as complete typescript functions).
* ### Usage
* ```typescript
* import dynamicProto from "@microsoft/dynamicproto-js";
* class ExampleClass extends BaseClass {
* constructor() {
* dynamicProto(ExampleClass, this, (_self, base) => {
* // This will define a function that will be converted to a prototype function
* _self.newFunc = () => {
* // Access any "this" instance property
* if (_self.someProperty) {
* ...
* }
* }
* // This will define a function that will be converted to a prototype function
* _self.myFunction = () => {
* // Access any "this" instance property
* if (_self.someProperty) {
* // Call the base version of the function that we are overriding
* base.myFunction();
* }
* ...
* }
* _self.initialize = () => {
* ...
* }
* // Warnings: While the following will work as _self is simply a reference to
* // this, if anyone overrides myFunction() the overridden will be called first
* // as the normal JavaScript method resolution will occur and the defined
* // _self.initialize() function is actually gets removed from the instance and
* // a proxy prototype version is created to reference the created method.
* _self.initialize();
* });
* }
* }
* ```
* @typeparam DPType This is the generic type of the class, used to keep intellisense valid
* @typeparam DPCls The type that contains the prototype of the current class
* @param theClass - This is the current class instance which contains the prototype for the current class
* @param target - The current "this" (target) reference, when the class has been extended this.prototype will not be the 'theClass' value.
* @param delegateFunc - The callback function (closure) that will create the dynamic function
* @param options - Additional options to configure how the dynamic prototype operates
*/
export default function dynamicProto<DPType, DPCls>(theClass:DPCls, target:DPType, delegateFunc: DynamicProtoDelegate<DPType>, options?:IDynamicProtoOpts): void {
// Make sure that the passed theClass argument looks correct
if (!objHasOwnProperty(theClass, Prototype)) {
_throwTypeError("theClass is an invalid class definition.");
}
// Quick check to make sure that the passed theClass argument looks correct (this is a common copy/paste error)
let classProto = theClass[Prototype];
if (!_checkPrototype(classProto, target)) {
_throwTypeError("[" + _getObjName(theClass) + "] not in hierarchy of [" + _getObjName(target) + "]");
}
let className = null;
if (objHasOwnProperty(classProto, DynClassName)) {
// Only grab the class name if it's defined on this prototype (i.e. don't walk the prototype chain)
className = classProto[DynClassName];
} else {
// As not all browser support name on the prototype creating a unique dynamic one if we have not already
// assigned one, so we can use a simple string as the lookup rather than an object for the dynamic instance
// function table lookup.
className = DynClassNamePrefix + _getObjName(theClass, "_") + "$" + _gblInst.n ;
_gblInst.n++;
classProto[DynClassName] = className;
}
let perfOptions = dynamicProto[DynProtoDefaultOptions];
let useBaseInst = !!perfOptions[strUseBaseInst];
if (useBaseInst && options && options[strUseBaseInst] !== undefined) {
useBaseInst = !!options[strUseBaseInst];
}
// Get the current instance functions
let instFuncs = _getInstanceFuncs(target);
// Get all of the functions for any base instance (before they are potentially overridden)
let baseFuncs = _getBaseFuncs(classProto, target, instFuncs, useBaseInst);
// Execute the delegate passing in both the current target "this" and "base" function references
// Note casting the same type as we don't actually have the base class here and this will provide some intellisense support
delegateFunc(target, baseFuncs as DPType);
// Don't allow setting instance functions for older IE instances
let setInstanceFunc = !!_objGetPrototypeOf && !!perfOptions[strSetInstFuncs];
if (setInstanceFunc && options) {
setInstanceFunc = !!options[strSetInstFuncs];
}
// Populate the Prototype for any overridden instance functions
_populatePrototype(classProto, className, target, instFuncs, setInstanceFunc !== false);
}
/**
* Exposes the default global options to allow global configuration, if the global values are disabled these will override
* any passed values. This is primarily exposed to support unit-testing without the need for individual classes to expose
* their internal usage of dynamic proto.
*/
dynamicProto[DynProtoDefaultOptions] = _gblInst.o;