• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16
17
18/**
19* @Observed class decorator
20*
21* usage:
22*    @Observed class ClassA { ... }
23*
24* Causes every instance of decorated clss to be automatically wrapped inside an ObservedObject.
25*
26* Implemented by extending the decroaetd class by class named 'ObservableObjectClass'.
27*
28* It is permisstable to decorate the base and the extended class like thisNote: I
29*   @Observed class ClassA { ...}
30*   @Observed class ClassB extends ClassA { ... }
31* and use
32*   a = new ClassA();
33*   b = new ClassB();
34* Only one ES6 Proxy is added.
35*
36*
37* Take note the decorator implementation extends the prototype chain.
38*
39* The prototype chain of a in above example is
40*  - ObservableObjectClass prototype
41*  - ClassA prototype
42*  - Object prototype
43*
44* Snd the prototype chain of b is
45*  - ObservableObjectClass prototype
46*  - ClassB prototype
47*  - ObservableObjectClass prototype
48*  - ClassA prototype
49*  - Object prototype
50*
51* The @Observed decorator is public, part of the SDK, starting from API 9.
52*
53*/
54
55
56// define just once to get just one Symbol
57const __IS_OBSERVED_PROXIED = Symbol('_____is_observed_proxied__');
58
59type Constructor = { new(...args: any[]): any };
60
61function Observed<T extends Constructor>(BaseClass: T): Constructor {
62  stateMgmtConsole.debug(`@Observed class decorator: Overwriting constructor for '${BaseClass.name}', gets wrapped inside ObservableObject proxy.`);
63
64  // prevent use of V3 @track inside V2 @Observed class
65  if (BaseClass.prototype && Reflect.has(BaseClass.prototype, ObserveV2.SYMBOL_REFS)) {
66    const error = `'@Observed class ${BaseClass?.name}': invalid use of V3 @track decorator inside V2 @Observed class. Need to fix class definition to use @Track.`;
67    stateMgmtConsole.error(error);
68    throw new Error(error);
69  }
70
71  return class extends BaseClass {
72    constructor(...args: any) {
73      super(...args);
74      stateMgmtConsole.debug(`@Observed '${BaseClass.name}' modified constructor.`);
75      ConfigureStateMgmt.instance.usingPUObservedTrack(`@Observed`, BaseClass.name);
76      let isProxied = Reflect.has(this, __IS_OBSERVED_PROXIED);
77      Object.defineProperty(this, __IS_OBSERVED_PROXIED, {
78        value: true,
79        enumerable: false,
80        configurable: false,
81        writable: false
82      });
83      if (isProxied) {
84        stateMgmtConsole.debug(`   ... new '${BaseClass.name}', is proxied already`);
85        return this;
86      } else {
87        stateMgmtConsole.debug(`   ... new '${BaseClass.name}', wrapping inside ObservedObject proxy`);
88        return ObservedObject.createNewInternal(this, undefined);
89      }
90    }
91  };
92}
93
94/**
95 * class ObservedObject and supporting Handler classes,
96 * Extends from ES6 Proxy. In adding to 'get' and 'set'
97 * the clasess manage subscribers that receive notification
98 * about proxies object being 'read' or 'changed'.
99 *
100 * These classes are framework internal / non-SDK
101 *
102 */
103
104type PropertyReadCbFunc = (readObject: Object, readPropName: string, isTracked: boolean) => void;
105
106class SubscribableHandler {
107  static readonly SUBSCRIBE = Symbol('_____subscribe__');
108  static readonly UNSUBSCRIBE = Symbol('_____unsubscribe__');
109  static readonly COUNT_SUBSCRIBERS = Symbol('____count_subscribers__');
110  static readonly SET_ONREAD_CB = Symbol('_____set_onread_cb__');
111  static readonly RAW_THIS = Symbol('_____raw_this');
112
113  private owningProperties_: Set<number>;
114  private readCbFunc_?: PropertyReadCbFunc;
115  private obSelf_?: ObservedPropertyAbstractPU<any>;
116
117  constructor(owningProperty: IPropertySubscriber) {
118    this.owningProperties_ = new Set<number>();
119
120    if (owningProperty) {
121      this.addOwningProperty(owningProperty);
122    }
123    stateMgmtConsole.debug(`SubscribableHandler: constructor done`);
124  }
125
126  private isPropertyTracked(obj: Object, property: string): boolean {
127    return Reflect.has(obj, `___TRACKED_${property}`) ||
128      property === TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY ||
129      property === TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY;
130  }
131
132  addOwningProperty(subscriber: IPropertySubscriber): void {
133    if (subscriber) {
134      stateMgmtConsole.debug(`SubscribableHandler: addOwningProperty: subscriber '${subscriber.id__()}'.`);
135      this.owningProperties_.add(subscriber.id__());
136    } else {
137      stateMgmtConsole.warn(`SubscribableHandler: addOwningProperty: undefined subscriber.`);
138    }
139  }
140
141  /*
142    the inverse function of createOneWaySync or createTwoWaySync
143   */
144  public removeOwningProperty(property: IPropertySubscriber): void {
145    return this.removeOwningPropertyById(property.id__());
146  }
147
148  public removeOwningPropertyById(subscriberId: number): void {
149    stateMgmtConsole.debug(`SubscribableHandler: removeOwningProperty '${subscriberId}'.`);
150    this.owningProperties_.delete(subscriberId);
151  }
152
153  protected notifyObjectPropertyHasChanged(propName: string, newValue: any) {
154    stateMgmtConsole.debug(`SubscribableHandler: notifyObjectPropertyHasChanged '${propName}'.`);
155    this.owningProperties_.forEach((subscribedId) => {
156      const owningProperty: IPropertySubscriber = SubscriberManager.Find(subscribedId);
157      if (!owningProperty) {
158        stateMgmtConsole.warn(`SubscribableHandler: notifyObjectPropertyHasChanged: unknown subscriber.'${subscribedId}' error!.`);
159        return;
160      }
161
162      // PU code path
163      if ('onTrackedObjectPropertyCompatModeHasChangedPU' in owningProperty) {
164        (owningProperty as unknown as ObservedObjectEventsPUReceiver<any>).onTrackedObjectPropertyCompatModeHasChangedPU(this, propName);
165        return;
166      }
167
168      // FU code path
169      if ('hasChanged' in owningProperty) {
170        (owningProperty as ISinglePropertyChangeSubscriber<any>).hasChanged(newValue);
171      }
172      if ('propertyHasChanged' in owningProperty) {
173        (owningProperty as IMultiPropertiesChangeSubscriber).propertyHasChanged(propName);
174      }
175    });
176  }
177
178  protected notifyTrackedObjectPropertyHasChanged(propName: string): void {
179    stateMgmtConsole.debug(`SubscribableHandler: notifyTrackedObjectPropertyHasChanged '@Track ${propName}'.`);
180    this.owningProperties_.forEach((subscribedId) => {
181      const owningProperty: IPropertySubscriber = SubscriberManager.Find(subscribedId);
182      if (owningProperty && 'onTrackedObjectPropertyHasChangedPU' in owningProperty) {
183        // PU code path with observed object property change tracking optimization
184        (owningProperty as unknown as ObservedObjectEventsPUReceiver<any>).onTrackedObjectPropertyHasChangedPU(this, propName);
185      } else {
186        stateMgmtConsole.warn(`SubscribableHandler: notifyTrackedObjectPropertyHasChanged: subscriber.'${subscribedId}' lacks method 'trackedObjectPropertyHasChangedPU' internal error!.`);
187      }
188    });
189    // no need to support FU code path when app uses @Track
190  }
191
192  public has(target: Object, property: PropertyKey): boolean {
193    stateMgmtConsole.debug(`SubscribableHandler: has '${property.toString()}'.`);
194    return (property === ObservedObject.__IS_OBSERVED_OBJECT) ? true : Reflect.has(target, property);
195  }
196
197  public get(target: Object, property: PropertyKey, receiver?: any): any {
198    switch (property) {
199      case ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT:
200        return target;
201        break;
202      case SubscribableHandler.COUNT_SUBSCRIBERS:
203        return this.owningProperties_.size;
204        break;
205      case ObserveV2.SYMBOL_REFS:
206      case ObserveV2.V2_DECO_META:
207      case ObserveV2.SYMBOL_MAKE_OBSERVED:
208        // return result unmonitored
209        return Reflect.get(target, property, receiver);
210        break;
211      default:
212        const result = Reflect.get(target, property, receiver);
213        let propertyStr : string = String(property);
214        if (this.readCbFunc_ && typeof result !== 'function' && this.obSelf_ !== undefined) {
215          let isTracked = this.isPropertyTracked(target, propertyStr);
216          stateMgmtConsole.debug(`SubscribableHandler: get ObservedObject property '${isTracked ? '@Track ' : ''}${propertyStr}' notifying read.`);
217          this.readCbFunc_.call(this.obSelf_, receiver, propertyStr, isTracked);
218        } else {
219          // result is function or in compatibility mode (in compat mode cbFunc will never be set)
220          stateMgmtConsole.debug(`SubscribableHandler: get ObservedObject property '${propertyStr}' not notifying read.`);
221        }
222        return result;
223        break;
224    }
225  }
226
227  public set(target: Object, property: PropertyKey, newValue: any): boolean {
228    switch (property) {
229      case SubscribableHandler.SUBSCRIBE:
230        // assignment obsObj[SubscribableHandler.SUBSCRIBE] = subscriber
231        this.addOwningProperty(newValue as IPropertySubscriber);
232        return true;
233        break;
234      case SubscribableHandler.UNSUBSCRIBE:
235        // assignment obsObj[SubscribableHandler.UNSUBSCRIBE] = subscriber
236        this.removeOwningProperty(newValue as IPropertySubscriber);
237        return true;
238        break;
239      case SubscribableHandler.SET_ONREAD_CB:
240        // assignment obsObj[SubscribableHandler.SET_ONREAD_CB] = readCallbackFunc
241        stateMgmtConsole.debug(`SubscribableHandler: setReadingProperty: ${TrackedObject.isCompatibilityMode(target) ? 'not used in compatibility mode' : newValue ? 'set new cb function' : 'unset cb function'}.`);
242        this.readCbFunc_ = TrackedObject.isCompatibilityMode(target) ? undefined : (newValue as (PropertyReadCbFunc | undefined));
243        return true;
244        break;
245      case SubscribableHandler.RAW_THIS:
246        this.obSelf_ = TrackedObject.isCompatibilityMode(target) ? undefined : newValue;
247        return true;
248        break;
249      default:
250        // this is added for stability test: Reflect.get target is not object
251        try {
252          if (Reflect.get(target, property) === newValue) {
253            return true;
254          }
255        } catch (error) {
256          ArkTools.print('SubscribableHandler: set', target);
257          stateMgmtConsole.error(`An error occurred in SubscribableHandler set, target type is: ${typeof target}, ${error.message}`);
258          throw error;
259        }
260        Reflect.set(target, property, newValue);
261        const propString = String(property);
262        if (TrackedObject.isCompatibilityMode(target)) {
263          stateMgmtConsole.debug(`SubscribableHandler: set ObservedObject property '${propString}' (object property tracking compatibility mode).`);
264          this.notifyObjectPropertyHasChanged(propString, newValue);
265        } else {
266          if (this.isPropertyTracked(target, propString)) {
267            stateMgmtConsole.debug(`SubscribableHandler: set ObservedObject property '@Track ${propString}'.`);
268            this.notifyTrackedObjectPropertyHasChanged(propString);
269          } else {
270            stateMgmtConsole.debug(`SubscribableHandler: set ObservedObject property '${propString}' (object property tracking mode) is NOT @Tracked!`);
271          }
272        }
273        return true;
274        break;
275    }
276
277    // unreachable
278    return false;
279  }
280}
281
282class SubscribableMapSetHandler extends SubscribableHandler {
283  constructor(owningProperty: IPropertySubscriber) {
284    super(owningProperty);
285  }
286
287  // In-place Map/Set modification functions
288  mutatingFunctions = new Set([
289    /*Map functions*/
290    'set', 'clear', 'delete',
291    /*Set functions*/
292    'add', 'clear', 'delete',
293  ]);
294  proxiedFunctions = new Set([
295    /*Map functions*/
296    'set',
297    /*Set functions*/
298    'add'
299  ]);
300
301  /**
302   * Get trap for Map/Set type proxy
303   * Functions that modify Map/Set in-place are intercepted and replaced with a function
304   * that executes the original function and notifies the handler of a change.
305   * @param target Original Map/Set object
306   * @param property
307   * @param receiver Proxied Map/Set object
308   * @returns
309   */
310  get(target, property, receiver) {
311    if (property === ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT) {
312      return target;
313    }
314
315    //receiver will fail for internal slot methods of Set and Map
316    //So assign the target as receiver in this case.
317    if (property === Symbol.iterator || property === 'size') {
318      receiver = target;
319    }
320
321    let ret = super.get(target, property, receiver);
322    if (ret && typeof ret === 'function') {
323      const self = this;
324      return function () {
325        // execute original function with given arguments
326        const result = ret.apply(target, arguments);
327        if (self.mutatingFunctions.has(property)) {
328          self.notifyObjectPropertyHasChanged(property, target);
329        }
330        // Only calls to inserting items can be chained, so returning the 'proxiedObject'
331        // ensures that when chain calls also 2nd function call operates on the proxied object.
332        // Otherwise return the original result of the function.
333        return self.proxiedFunctions.has(property) ? receiver : result;
334      }.bind(receiver);
335    }
336
337    return ret;
338  }
339}
340
341class SubscribableDateHandler extends SubscribableHandler {
342
343  constructor(owningProperty: IPropertySubscriber) {
344    super(owningProperty);
345  }
346
347  dateSetFunctions = new Set(['setFullYear', 'setMonth', 'setDate', 'setHours', 'setMinutes', 'setSeconds',
348    'setMilliseconds', 'setTime', 'setUTCFullYear', 'setUTCMonth', 'setUTCDate', 'setUTCHours', 'setUTCMinutes',
349    'setUTCSeconds', 'setUTCMilliseconds']);
350
351  /**
352   * Get trap for Date type proxy
353   * Functions that modify Date in-place are intercepted and replaced with a function
354   * that executes the original function and notifies the handler of a change.
355   * @param target Original Date object
356   * @param property
357   * @returns
358   */
359  public get(target, property): any {
360    let ret = super.get(target, property);
361
362    if (typeof ret === 'function') {
363      if (this.dateSetFunctions.has(property)) {
364        const self = this;
365        return function () {
366          // execute original function with given arguments
367          let result = ret.apply(this, arguments);
368          self.notifyObjectPropertyHasChanged(property.toString(), this);
369          return result;
370          // bind 'this' to target inside the function
371        }.bind(target)
372      }
373      return ret.bind(target);
374    }
375    return ret;
376  }
377}
378
379class SubscribableArrayHandler extends SubscribableHandler {
380  constructor(owningProperty: IPropertySubscriber) {
381    super(owningProperty);
382  }
383
384  // In-place array modification functions
385  mutatingFunctions = new Set(['splice', 'copyWithin', 'fill', 'reverse', 'sort']);
386  // 'splice' and 'pop' self modifies the array, returns deleted array items
387  // means, alike other self-modifying functions, splice does not return the array itself.
388  specialFunctions = new Set(['splice', 'pop']);
389
390  /**
391   * Get trap for Array type proxy
392   * Functions that modify Array in-place are intercepted and replaced with a function
393   * that executes the original function and notifies the handler of a change.
394   * @param target Original Array object
395   * @param property
396   * @param receiver Proxied Array object
397   * @returns
398   */
399  get(target, property, receiver) {
400    if (property === ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT) {
401      return target;
402    }
403
404    let ret = super.get(target, property, receiver);
405    if (ret && typeof ret === 'function') {
406      const self = this;
407      const prop = property.toString();
408      if (self.mutatingFunctions.has(prop)) {
409        return function () {
410          const result = ret.apply(target, arguments);
411          // prop is the function name here
412          // and result is the function return value
413          // function modifies none or more properties
414          self.notifyObjectPropertyHasChanged(prop, self.specialFunctions.has(prop) ? target : result);
415          // returning the 'receiver(proxied object)' ensures that when chain calls also 2nd function call
416          // operates on the proxied object.
417          return self.specialFunctions.has(prop) ? result : receiver;
418        }.bind(receiver);
419      }
420      // binding the proxiedObject ensures that modifying functions like push() operate on the
421      // proxied array and each array change is notified.
422      return ret.bind(receiver);
423    }
424    return ret;
425  }
426}
427
428
429class ExtendableProxy {
430  constructor(obj: Object, handler: SubscribableHandler) {
431    return new Proxy(obj, handler);
432  }
433}
434
435class ObservedObject<T extends Object> extends ExtendableProxy {
436
437  /**
438   * Factory function for ObservedObjects /
439   *  wrapping of objects for proxying
440   *
441   * @param rawObject unproxied Object or ObservedObject
442   * @param objOwner owner of this Object to sign uop for propertyChange
443   *          notifications
444   * @returns the rawObject if object is already an ObservedObject,
445   *          otherwise the newly created ObservedObject
446   */
447  public static createNew<T extends Object>(rawObject: T,
448    owningProperty: IPropertySubscriber): T {
449
450    if (rawObject === null || rawObject === undefined) {
451      stateMgmtConsole.error(`ObservedObject.CreateNew, input object must not be null or undefined.`);
452      return rawObject;
453    }
454
455    if (ObservedObject.IsObservedObject(rawObject)) {
456      ObservedObject.addOwningProperty(rawObject, owningProperty);
457      return rawObject;
458    }
459
460    return ObservedObject.createNewInternal<T>(rawObject, owningProperty);
461  }
462
463  public static createNewInternal<T extends Object>(rawObject: T,
464    owningProperty: IPropertySubscriber): T {
465    let proxiedObject;
466    if (rawObject instanceof Map || rawObject instanceof Set) {
467      proxiedObject = new ObservedObject<T>(rawObject, new SubscribableMapSetHandler(owningProperty), owningProperty);
468    }
469    else if (rawObject instanceof Date) {
470      proxiedObject = new ObservedObject<T>(rawObject, new SubscribableDateHandler(owningProperty), owningProperty);
471    }
472    else if (Array.isArray(rawObject)) {
473      proxiedObject = new ObservedObject<T>(rawObject, new SubscribableArrayHandler(owningProperty), owningProperty);
474    }
475    else {
476      proxiedObject = new ObservedObject(rawObject, new SubscribableHandler(owningProperty), owningProperty);
477    }
478    return proxiedObject as T;
479  }
480
481  /*
482    Return the unproxied object 'inside' the ObservedObject / the ES6 Proxy
483    no set observation, no notification of changes!
484    Use with caution, do not store any references
485  */
486  static GetRawObject<T extends Object>(obj: T): T {
487    return !ObservedObject.IsObservedObject(obj) ? obj : obj[ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT];
488  }
489
490  /**
491   *
492   * @param obj anything
493   * @returns true if the parameter is an Object wrpped with a ObservedObject
494   * Note: Since ES6 Proying is transparent, 'instance of' will not work. Use
495   * this static function instead.
496   */
497  static IsObservedObject(obj: any): boolean {
498    return (obj && (typeof obj === 'object') && Reflect.has(obj, ObservedObject.__IS_OBSERVED_OBJECT));
499  }
500
501  /**
502   * add a subscriber to given ObservedObject
503   * due to the proxy nature this static method approach needs to be used instead of a member
504   * function
505   * @param obj
506   * @param subscriber
507   * @returns false if given object is not an ObservedObject
508   */
509  public static addOwningProperty(obj: Object, subscriber: IPropertySubscriber): boolean {
510    if (!ObservedObject.IsObservedObject(obj) || !subscriber) {
511      return false;
512    }
513
514    obj[SubscribableHandler.SUBSCRIBE] = subscriber;
515    return true;
516  }
517
518  /**
519   * remove a subscriber to given ObservedObject
520   * due to the proxy nature this static method approach needs to be used instead of a member
521   * function
522   * @param obj
523   * @param subscriber
524   * @returns false if given object is not an ObservedObject
525   */
526  public static removeOwningProperty(obj: Object,
527    subscriber: IPropertySubscriber): boolean {
528    if (!ObservedObject.IsObservedObject(obj)) {
529      return false;
530    }
531
532    obj[SubscribableHandler.UNSUBSCRIBE] = subscriber;
533    return true;
534  }
535
536  /**
537   *
538   * @param obj any Object
539   * @returns return number of subscribers to the given ObservedObject
540   * or false if given object is not an ObservedObject
541   */
542  public static countSubscribers(obj: Object): number | false {
543    return ObservedObject.IsObservedObject(obj) ? obj[SubscribableHandler.COUNT_SUBSCRIBERS] : false;
544  }
545
546  /*
547    set or unset callback function to be called when a property has been called
548  */
549  public static registerPropertyReadCb(obj: Object, readPropCb: PropertyReadCbFunc, obSelf: ObservedPropertyAbstractPU<any>): boolean {
550    if (!ObservedObject.IsObservedObject(obj)) {
551      return false;
552    }
553    obj[SubscribableHandler.SET_ONREAD_CB] = readPropCb;
554    obj[SubscribableHandler.RAW_THIS] = obSelf;
555    return true;
556  }
557
558  public static unregisterPropertyReadCb(obj: Object): boolean {
559    if (!ObservedObject.IsObservedObject(obj)) {
560      return false;
561    }
562    obj[SubscribableHandler.SET_ONREAD_CB] = undefined;
563    obj[SubscribableHandler.RAW_THIS] = undefined;
564    return true;
565  }
566
567
568  /**
569   * Utility function for debugging the prototype chain of given Object
570   * The given object can be any Object, it is not required to be an ObservedObject
571   * @param object
572   * @returns multi-line string containing info about the prototype chain
573   * on class in class hiararchy per line
574   */
575  public static tracePrototypeChainOfObject(object: Object | undefined): string {
576    let proto = Object.getPrototypeOf(object);
577    let result = '';
578    let sepa = '';
579    while (proto) {
580      result += `${sepa}${ObservedObject.tracePrototype(proto)}`;
581      proto = Object.getPrototypeOf(proto);
582      sepa = ',\n';
583    }
584
585    return result;
586  }
587
588  /**
589   * Utility function for debugging all functions of given Prototype.
590   * @returns string containing containing names of all functions and members of given Prototype
591   */
592  public static tracePrototype(proto: any) {
593    if (!proto) {
594      return '';
595    }
596
597    let result = `${proto.constructor && proto.constructor.name ? proto.constructor.name : '<no class>'}: `;
598    let sepa = '';
599    for (let name of Object.getOwnPropertyNames(proto)) {
600      result += `${sepa}${name}`;
601      sepa = ', ';
602    };
603    return result;
604  }
605
606
607  /**
608   * @Observed  decorator extends the decorated class. This function returns the prototype of the decorated class
609   * @param proto
610   * @returns prototype of the @Observed decorated class or 'proto' parameter if not  @Observed decorated
611   */
612  public static getPrototypeOfObservedClass(proto: Object): Object {
613    return (proto.constructor && proto.constructor.name === 'ObservedClass')
614      ? Object.getPrototypeOf(proto.constructor.prototype)
615      : proto;
616  }
617
618
619  /**
620   * To create a new ObservableObject use CreateNew function
621   *
622   * constructor create a new ObservableObject and subscribe its owner to propertyHasChanged
623   * notifications
624   * @param obj  raw Object, if obj is a ObservableOject throws an error
625   * @param objectOwner
626   */
627  private constructor(obj: T, handler: SubscribableHandler, objectOwningProperty: IPropertySubscriber) {
628    super(obj, handler);
629
630    if (ObservedObject.IsObservedObject(obj)) {
631      stateMgmtConsole.error('ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already');
632    }
633    if (objectOwningProperty) {
634      this[SubscribableHandler.SUBSCRIBE] = objectOwningProperty;
635    }
636  } // end of constructor
637
638  public static readonly __IS_OBSERVED_OBJECT = Symbol('_____is_observed_object__');
639  public static readonly __OBSERVED_OBJECT_RAW_OBJECT = Symbol('_____raw_object__');
640}
641