| /* |
| * Copyright (C) 2015 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WI.PropertyPath = class PropertyPath |
| { |
| constructor(object, pathComponent, parent, isPrototype) |
| { |
| console.assert(object instanceof WI.RemoteObject || object === null); |
| console.assert(!pathComponent || typeof pathComponent === "string"); |
| console.assert(!parent || parent instanceof WI.PropertyPath); |
| console.assert(!parent || pathComponent.length); |
| |
| // We allow property pathes with null objects as end-caps only. |
| // Disallow appending to a PropertyPath with null objects. |
| if (parent && !parent.object) |
| throw new Error("Attempted to append to a PropertyPath with null object."); |
| |
| this._object = object; |
| this._pathComponent = typeof pathComponent === "string" ? pathComponent : null; |
| this._parent = parent || null; |
| this._isPrototype = isPrototype || false; |
| } |
| |
| // Static |
| |
| static emptyPropertyPathForScope(object) |
| { |
| return new WI.PropertyPath(object, WI.PropertyPath.SpecialPathComponent.EmptyPathComponentForScope); |
| } |
| |
| // Public |
| |
| get object() { return this._object; } |
| get parent() { return this._parent; } |
| get isPrototype() { return this._isPrototype; } |
| get pathComponent() { return this._pathComponent; } |
| |
| get rootObject() |
| { |
| return this._parent ? this._parent.rootObject : this._object; |
| } |
| |
| get lastNonPrototypeObject() |
| { |
| if (!this._parent) |
| return this._object; |
| |
| var p = this._parent; |
| while (p) { |
| if (!p.isPrototype) |
| break; |
| if (!p.parent) |
| break; |
| p = p.parent; |
| } |
| |
| return p.object; |
| } |
| |
| get fullPath() |
| { |
| var components = []; |
| for (var p = this; p && p.pathComponent; p = p.parent) |
| components.push(p.pathComponent); |
| components.reverse(); |
| return components.join(""); |
| } |
| |
| get reducedPath() |
| { |
| // The display path for a value should not include __proto__. |
| // The path for "foo.__proto__.bar.__proto__.x" is better shown as "foo.bar.x". |
| // FIXME: We should keep __proto__ if this property was overridden. |
| var components = []; |
| |
| var p = this; |
| |
| // Include trailing __proto__s. |
| for (; p && p.isPrototype; p = p.parent) |
| components.push(p.pathComponent); |
| |
| // Skip other __proto__s. |
| for (; p && p.pathComponent; p = p.parent) { |
| if (p.isPrototype) |
| continue; |
| components.push(p.pathComponent); |
| } |
| |
| components.reverse(); |
| return components.join(""); |
| } |
| |
| displayPath(type) |
| { |
| return type === WI.PropertyPath.Type.Value ? this.reducedPath : this.fullPath; |
| } |
| |
| isRoot() |
| { |
| return !this._parent; |
| } |
| |
| isScope() |
| { |
| return this._pathComponent === WI.PropertyPath.SpecialPathComponent.EmptyPathComponentForScope; |
| } |
| |
| isPathComponentImpossible() |
| { |
| return this._pathComponent && this._pathComponent.startsWith("@"); |
| } |
| |
| isFullPathImpossible() |
| { |
| if (this.isPathComponentImpossible()) |
| return true; |
| |
| if (this._parent) |
| return this._parent.isFullPathImpossible(); |
| |
| return false; |
| } |
| |
| appendPropertyName(object, propertyName) |
| { |
| var isPrototype = propertyName === "__proto__"; |
| |
| if (this.isScope()) |
| return new WI.PropertyPath(object, propertyName, this, isPrototype); |
| |
| var component = this._canPropertyNameBeDotAccess(propertyName) ? "." + propertyName : "[" + doubleQuotedString(propertyName) + "]"; |
| return new WI.PropertyPath(object, component, this, isPrototype); |
| } |
| |
| appendPropertySymbol(object, symbolName) |
| { |
| var component = WI.PropertyPath.SpecialPathComponent.SymbolPropertyName + (symbolName.length ? "(" + symbolName + ")" : ""); |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| appendInternalPropertyName(object, propertyName) |
| { |
| var component = WI.PropertyPath.SpecialPathComponent.InternalPropertyName + "[" + propertyName + "]"; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| appendGetterPropertyName(object, propertyName) |
| { |
| var component = ".__lookupGetter__(" + doubleQuotedString(propertyName) + ")"; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| appendSetterPropertyName(object, propertyName) |
| { |
| var component = ".__lookupSetter__(" + doubleQuotedString(propertyName) + ")"; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| appendArrayIndex(object, indexString) |
| { |
| var component = "[" + indexString + "]"; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| appendMapKey(object) |
| { |
| var component = WI.PropertyPath.SpecialPathComponent.MapKey; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| appendMapValue(object, keyObject) |
| { |
| console.assert(!keyObject || keyObject instanceof WI.RemoteObject); |
| |
| if (keyObject && keyObject.hasValue()) { |
| if (keyObject.type === "string") { |
| var component = ".get(" + doubleQuotedString(keyObject.description) + ")"; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| var component = ".get(" + keyObject.description + ")"; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| var component = WI.PropertyPath.SpecialPathComponent.MapValue; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| appendSetIndex(object) |
| { |
| var component = WI.PropertyPath.SpecialPathComponent.SetIndex; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| appendSymbolProperty(object) |
| { |
| var component = WI.PropertyPath.SpecialPathComponent.SymbolPropertyName; |
| return new WI.PropertyPath(object, component, this); |
| } |
| |
| appendPropertyDescriptor(object, descriptor, type) |
| { |
| console.assert(descriptor instanceof WI.PropertyDescriptor); |
| |
| if (descriptor.isInternalProperty) |
| return this.appendInternalPropertyName(object, descriptor.name); |
| if (descriptor.symbol) |
| return this.appendSymbolProperty(object); |
| |
| if (type === WI.PropertyPath.Type.Getter) |
| return this.appendGetterPropertyName(object, descriptor.name); |
| if (type === WI.PropertyPath.Type.Setter) |
| return this.appendSetterPropertyName(object, descriptor.name); |
| |
| console.assert(type === WI.PropertyPath.Type.Value); |
| |
| if (this._object.subtype === "array" && !isNaN(parseInt(descriptor.name))) |
| return this.appendArrayIndex(object, descriptor.name); |
| |
| return this.appendPropertyName(object, descriptor.name); |
| } |
| |
| // Private |
| |
| _canPropertyNameBeDotAccess(propertyName) |
| { |
| return /^(?![0-9])\w+$/.test(propertyName); |
| } |
| }; |
| |
| WI.PropertyPath.SpecialPathComponent = { |
| InternalPropertyName: "@internal", |
| SymbolPropertyName: "@symbol", |
| MapKey: "@mapkey", |
| MapValue: "@mapvalue", |
| SetIndex: "@setindex", |
| EmptyPathComponentForScope: "", |
| }; |
| |
| WI.PropertyPath.Type = { |
| Value: "value", |
| Getter: "getter", |
| Setter: "setter", |
| }; |