aboutsummaryrefslogtreecommitdiff
path: root/contexts/data/lib/closure-library/closure/goog/debug/reflect.js
blob: 72f6d2783399e298228847cf782b49add8f8e7cb (plain)
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
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview JavaScript reflection tools. They should only be used for
 * debugging non-compiled code or tests, because there is no guarantee that
 * they work consistently in all browsers.
 *
 */

goog.provide('goog.debug.reflect');


/**
 * Maps the unique id of the known constructors to their full names.
 * Initialized lazily.
 * @type {Object.<number, string>}
 * @private
 */
goog.debug.reflect.typeMap_ = null;


/**
 * List of all known constructors. Initialized lazily.
 * @type {Array.<!Function>}
 * @private
 */
goog.debug.reflect.constructors_ = null;


/**
 * Copy of {@code Object.prototype.toString} to use if it is overridden later.
 * Although saving the original {@code toString} somewhat protects against
 * third-party libraries which touch {@code Object.prototype}, the actual goal
 * of this assignment is to allow overriding that method, thus more debug
 * information can be exposed about objects.
 * See {@link goog.debug.reflect.typeOf}.
 * @private
 */
goog.debug.reflect.toString_ = Object.prototype.toString;


/**
 * Registers a type which will be recognized by goog.debug.reflect.typeOf.
 * @param {string} name Full name of the type.
 * @param {!Function} ctor The constructor.
 * @private
 */
goog.debug.reflect.registerType_ = function(name, ctor) {
  goog.debug.reflect.constructors_.push(ctor);
  goog.debug.reflect.typeMap_[goog.getUid(ctor)] = name;
};


/**
 * Adds all known constructors to the type registry.
 * @private
 */
goog.debug.reflect.init_ = function() {
  if (goog.debug.reflect.typeMap_) {
    return;
  }

  goog.debug.reflect.typeMap_ = {};
  goog.debug.reflect.constructors_ = [];
  var implicitNs = goog.getObjectByName('goog.implicitNamespaces_') || {};

  for (var ns in implicitNs) {
    if (implicitNs.hasOwnProperty(ns)) {
      var nsObj = goog.getObjectByName(ns);
      for (var name in nsObj) {
        if (nsObj.hasOwnProperty(name) && goog.isFunction(nsObj[name])) {
          goog.debug.reflect.registerType_(ns + '.' + name, nsObj[name]);
        }
      }
    }
  }

  goog.debug.reflect.registerType_('Array', Array);
  goog.debug.reflect.registerType_('Boolean', Boolean);
  goog.debug.reflect.registerType_('Date', Date);
  goog.debug.reflect.registerType_('Error', Error);
  goog.debug.reflect.registerType_('Function', Function);
  goog.debug.reflect.registerType_('Number', Number);
  goog.debug.reflect.registerType_('Object', Object);
  goog.debug.reflect.registerType_('String', String);

  // The compiler gets upset if we alias regexp directly, because
  // then it can't optimize regexps as well. Just be sneaky about it,
  // because this is only for debugging.
  goog.debug.reflect.registerType_('RegExp', goog.global['RegExp']);
};


/**
 * Returns the name of a type of object.
 * @param {!Function} classConstructor A object constructor to get the name of.
 * @return {string|undefined} The string name of the class.
 */
goog.debug.reflect.className = function(classConstructor) {
  goog.debug.reflect.init_();
  if (goog.isDefAndNotNull(classConstructor)) {
    return goog.debug.reflect.typeMap_[goog.getUid(classConstructor)];
  } else {
    return undefined;
  }
};


/**
 * Guesses the real type of the object, even if its {@code toString} method is
 * overridden. Gives exact result for all goog.provided classes in non-compiled
 * code, and some often used native classes in compiled code too. Not tested in
 * multi-frame environment.
 *
 * Example use case to get better type information in the Watch tab of FireBug:
 * <pre>
 * Object.prototype.toString = function() {
 *   return goog.debug.reflect.typeOf(this);
 * };
 * </pre>
 *
 * @param {*} obj An arbitrary variable to get the type of.
 * @return {string} The namespaced type of the argument or 'Object' if didn't
 *     manage to determine it. Warning: in IE7 ActiveX (including DOM) objects
 *     don't expose their type to JavaScript. Their {@code constructor}
 *     property is undefined and they are not even the instances of the
 *     {@code Object} type. This method will recognize them as 'ActiveXObject'.
 */
goog.debug.reflect.typeOf = function(obj) {
  // Check primitive types.
  if (!obj || goog.isNumber(obj) || goog.isString(obj) || goog.isBoolean(obj)) {
    return goog.typeOf(obj);
  }

  // Check if the type is present in the registry.
  goog.debug.reflect.init_();
  if (obj.constructor) {
    // Some DOM objects such as document don't have constructor in IE7.
    var type = goog.debug.reflect.typeMap_[goog.getUid(obj.constructor)];
    if (type) {
      return type;
    }
  }

  // In IE8 the internal 'class' property of ActiveXObjects is Object, but
  // String(obj) tells their real type.
  var isActiveXObject = goog.global.ActiveXObject &&
      obj instanceof ActiveXObject;
  var typeString = isActiveXObject ? String(obj) :
      goog.debug.reflect.toString_.call(/** @type {Object} */ (obj));
  var match = typeString.match(/^\[object (\w+)\]$/);
  if (match) {
    var name = match[1];
    var ctor = goog.global[name];
    try {
      if (obj instanceof ctor) {
        return name;
      }
    } catch (e) {
      // instanceof may fail if the guessed name is not a real type.
    }
  }

  // Fall back to Object or ActiveXObject.
  return isActiveXObject ? 'ActiveXObject' : 'Object';
};