// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
// OWNER OR 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.
/**
* @fileoverview Utilities to debug JSPB based proto objects.
*/
goog.provide('jspb.debug');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.object');
goog.require('jspb.Message');
/**
* Turns a proto into a human readable object that can i.e. be written to the
* console: {@code console.log(jspb.debug.dump(myProto))}.
* This function makes a best effort and may not work in all cases. It will not
* work in obfuscated and or optimized code.
* Use this in environments where {@see jspb.Message.prototype.toObject} is
* not available for code size reasons.
* @param {jspb.Message} message A jspb.Message.
* @return {Object}
*/
jspb.debug.dump = function(message) {
if (!goog.DEBUG) {
return null;
}
goog.asserts.assert(message instanceof jspb.Message,
'jspb.Message instance expected');
/** @type {Object} */
var object = message;
goog.asserts.assert(object['getExtension'],
'Only unobfuscated and unoptimized compilation modes supported.');
return /** @type {Object} */ (jspb.debug.dump_(message));
};
/**
* Recursively introspects a message and the values its getters return to
* make a best effort in creating a human readable representation of the
* message.
* @param {?} thing A jspb.Message, Array or primitive type to dump.
* @return {*}
* @private
*/
jspb.debug.dump_ = function(thing) {
var type = goog.typeOf(thing);
if (type == 'number' || type == 'string' || type == 'boolean' ||
type == 'null' || type == 'undefined') {
return thing;
}
if (type == 'array') {
goog.asserts.assertArray(thing);
return goog.array.map(thing, jspb.debug.dump_);
}
var message = thing; // Copy because we don't want type inference on thing.
goog.asserts.assert(message instanceof jspb.Message,
'Only messages expected: ' + thing);
var ctor = message.constructor;
var messageName = ctor.name || ctor.displayName;
var object = {
'$name': messageName
};
for (var name in ctor.prototype) {
var match = /^get([A-Z]\w*)/.exec(name);
if (match && name != 'getExtension' &&
name != 'getJsPbMessageId') {
var val = thing[name]();
if (val != null) {
object[jspb.debug.formatFieldName_(match[1])] = jspb.debug.dump_(val);
}
}
}
if (COMPILED && thing['extensionObject_']) {
object['$extensions'] = 'Recursive dumping of extensions not supported ' +
'in compiled code. Switch to uncompiled or dump extension object ' +
'directly';
return object;
}
var extensionsObject;
for (var id in ctor['extensions']) {
if (/^\d+$/.test(id)) {
var ext = ctor['extensions'][id];
var extVal = thing.getExtension(ext);
var fieldName = goog.object.getKeys(ext.fieldName)[0];
if (extVal != null) {
if (!extensionsObject) {
extensionsObject = object['$extensions'] = {};
}
extensionsObject[jspb.debug.formatFieldName_(fieldName)] =
jspb.debug.dump_(extVal);
}
}
}
return object;
};
/**
* Formats a field name for output as camelCase.
*
* @param {string} name Name of the field.
* @return {string}
* @private
*/
jspb.debug.formatFieldName_ = function(name) {
// Name may be in TitleCase.
return name.replace(/^[A-Z]/, function(c) {
return c.toLowerCase();
});
};