From a3c0f200354a95a1217e93ef600d34a6f0c8d6d9 Mon Sep 17 00:00:00 2001 From: "mikhail.naganov@gmail.com" Date: Fri, 15 Jan 2010 15:34:32 +0000 Subject: [PATCH] Submit Object.getOwnPropertyNames patch by Pavel Feldman. See http://codereview.chromium.org/549050. Add copyright to regression test to fix build broken by r3619. TBR=sgjesse@chromium.org Review URL: http://codereview.chromium.org/542092 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3620 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/messages.js | 3 +- src/mirror-delay.js | 12 +- src/runtime.cc | 300 +++++++++++++------------- src/runtime.h | 10 +- src/v8natives.js | 55 ++++- test/mjsunit/object-get-own-property-names.js | 104 +++++++++ test/mjsunit/regress/regress-crbug-3184.js | 27 +++ 7 files changed, 339 insertions(+), 172 deletions(-) create mode 100644 test/mjsunit/object-get-own-property-names.js diff --git a/src/messages.js b/src/messages.js index d3c8fcc..df008c9 100644 --- a/src/messages.js +++ b/src/messages.js @@ -178,8 +178,7 @@ function FormatMessage(message) { result_not_primitive: "Result of %0 must be a primitive, was %1", invalid_json: "String '%0' is not valid JSON", circular_structure: "Converting circular structure to JSON", - object_keys_non_object: "Object.keys called on non-object", - object_get_prototype_non_object: "Object.getPrototypeOf called on non-object", + obj_ctor_property_non_object: "Object.%0 called on non-object", array_indexof_not_defined: "Array.getIndexOf: Argument undefined" }; } diff --git a/src/mirror-delay.js b/src/mirror-delay.js index ba663b2..0269f1f 100644 --- a/src/mirror-delay.js +++ b/src/mirror-delay.js @@ -600,14 +600,14 @@ ObjectMirror.prototype.protoObject = function() { ObjectMirror.prototype.hasNamedInterceptor = function() { // Get information on interceptors for this object. - var x = %DebugInterceptorInfo(this.value_); + var x = %GetInterceptorInfo(this.value_); return (x & 2) != 0; }; ObjectMirror.prototype.hasIndexedInterceptor = function() { // Get information on interceptors for this object. - var x = %DebugInterceptorInfo(this.value_); + var x = %GetInterceptorInfo(this.value_); return (x & 1) != 0; }; @@ -631,13 +631,13 @@ ObjectMirror.prototype.propertyNames = function(kind, limit) { // Find all the named properties. if (kind & PropertyKind.Named) { // Get the local property names. - propertyNames = %DebugLocalPropertyNames(this.value_); + propertyNames = %GetLocalPropertyNames(this.value_); total += propertyNames.length; // Get names for named interceptor properties if any. if (this.hasNamedInterceptor() && (kind & PropertyKind.Named)) { var namedInterceptorNames = - %DebugNamedInterceptorPropertyNames(this.value_); + %GetNamedInterceptorPropertyNames(this.value_); if (namedInterceptorNames) { propertyNames = propertyNames.concat(namedInterceptorNames); total += namedInterceptorNames.length; @@ -648,13 +648,13 @@ ObjectMirror.prototype.propertyNames = function(kind, limit) { // Find all the indexed properties. if (kind & PropertyKind.Indexed) { // Get the local element names. - elementNames = %DebugLocalElementNames(this.value_); + elementNames = %GetLocalElementNames(this.value_); total += elementNames.length; // Get names for indexed interceptor properties. if (this.hasIndexedInterceptor() && (kind & PropertyKind.Indexed)) { var indexedInterceptorNames = - %DebugIndexedInterceptorElementNames(this.value_); + %GetIndexedInterceptorElementNames(this.value_); if (indexedInterceptorNames) { elementNames = elementNames.concat(indexedInterceptorNames); total += indexedInterceptorNames.length; diff --git a/src/runtime.cc b/src/runtime.cc index 617dfaa..5c45692 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -3213,6 +3213,156 @@ static Object* Runtime_GetPropertyNamesFast(Arguments args) { } +// Find the length of the prototype chain that is to to handled as one. If a +// prototype object is hidden it is to be viewed as part of the the object it +// is prototype for. +static int LocalPrototypeChainLength(JSObject* obj) { + int count = 1; + Object* proto = obj->GetPrototype(); + while (proto->IsJSObject() && + JSObject::cast(proto)->map()->is_hidden_prototype()) { + count++; + proto = JSObject::cast(proto)->GetPrototype(); + } + return count; +} + + +// Return the names of the local named properties. +// args[0]: object +static Object* Runtime_GetLocalPropertyNames(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 1); + if (!args[0]->IsJSObject()) { + return Heap::undefined_value(); + } + CONVERT_ARG_CHECKED(JSObject, obj, 0); + + // Skip the global proxy as it has no properties and always delegates to the + // real global object. + if (obj->IsJSGlobalProxy()) { + obj = Handle(JSObject::cast(obj->GetPrototype())); + } + + // Find the number of objects making up this. + int length = LocalPrototypeChainLength(*obj); + + // Find the number of local properties for each of the objects. + int* local_property_count = NewArray(length); + int total_property_count = 0; + Handle jsproto = obj; + for (int i = 0; i < length; i++) { + int n; + n = jsproto->NumberOfLocalProperties(static_cast(NONE)); + local_property_count[i] = n; + total_property_count += n; + if (i < length - 1) { + jsproto = Handle(JSObject::cast(jsproto->GetPrototype())); + } + } + + // Allocate an array with storage for all the property names. + Handle names = Factory::NewFixedArray(total_property_count); + + // Get the property names. + jsproto = obj; + int proto_with_hidden_properties = 0; + for (int i = 0; i < length; i++) { + jsproto->GetLocalPropertyNames(*names, + i == 0 ? 0 : local_property_count[i - 1]); + if (!GetHiddenProperties(jsproto, false)->IsUndefined()) { + proto_with_hidden_properties++; + } + if (i < length - 1) { + jsproto = Handle(JSObject::cast(jsproto->GetPrototype())); + } + } + + // Filter out name of hidden propeties object. + if (proto_with_hidden_properties > 0) { + Handle old_names = names; + names = Factory::NewFixedArray( + names->length() - proto_with_hidden_properties); + int dest_pos = 0; + for (int i = 0; i < total_property_count; i++) { + Object* name = old_names->get(i); + if (name == Heap::hidden_symbol()) { + continue; + } + names->set(dest_pos++, name); + } + } + + DeleteArray(local_property_count); + return *Factory::NewJSArrayWithElements(names); +} + + +// Return the names of the local indexed properties. +// args[0]: object +static Object* Runtime_GetLocalElementNames(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 1); + if (!args[0]->IsJSObject()) { + return Heap::undefined_value(); + } + CONVERT_ARG_CHECKED(JSObject, obj, 0); + + int n = obj->NumberOfLocalElements(static_cast(NONE)); + Handle names = Factory::NewFixedArray(n); + obj->GetLocalElementKeys(*names, static_cast(NONE)); + return *Factory::NewJSArrayWithElements(names); +} + + +// Return information on whether an object has a named or indexed interceptor. +// args[0]: object +static Object* Runtime_GetInterceptorInfo(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 1); + if (!args[0]->IsJSObject()) { + return Smi::FromInt(0); + } + CONVERT_ARG_CHECKED(JSObject, obj, 0); + + int result = 0; + if (obj->HasNamedInterceptor()) result |= 2; + if (obj->HasIndexedInterceptor()) result |= 1; + + return Smi::FromInt(result); +} + + +// Return property names from named interceptor. +// args[0]: object +static Object* Runtime_GetNamedInterceptorPropertyNames(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 1); + CONVERT_ARG_CHECKED(JSObject, obj, 0); + + if (obj->HasNamedInterceptor()) { + v8::Handle result = GetKeysForNamedInterceptor(obj, obj); + if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result); + } + return Heap::undefined_value(); +} + + +// Return element names from indexed interceptor. +// args[0]: object +static Object* Runtime_GetIndexedInterceptorElementNames(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 1); + CONVERT_ARG_CHECKED(JSObject, obj, 0); + + if (obj->HasIndexedInterceptor()) { + v8::Handle result = GetKeysForIndexedInterceptor(obj, obj); + if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result); + } + return Heap::undefined_value(); +} + + static Object* Runtime_LocalKeys(Arguments args) { ASSERT_EQ(args.length(), 1); CONVERT_CHECKED(JSObject, raw_object, args[0]); @@ -6005,21 +6155,6 @@ static Object* Runtime_Break(Arguments args) { } -// Find the length of the prototype chain that is to to handled as one. If a -// prototype object is hidden it is to be viewed as part of the the object it -// is prototype for. -static int LocalPrototypeChainLength(JSObject* obj) { - int count = 1; - Object* proto = obj->GetPrototype(); - while (proto->IsJSObject() && - JSObject::cast(proto)->map()->is_hidden_prototype()) { - count++; - proto = JSObject::cast(proto)->GetPrototype(); - } - return count; -} - - static Object* DebugLookupResultValue(Object* receiver, String* name, LookupResult* result, bool* caught_exception) { @@ -6189,93 +6324,6 @@ static Object* Runtime_DebugGetProperty(Arguments args) { } -// Return the names of the local named properties. -// args[0]: object -static Object* Runtime_DebugLocalPropertyNames(Arguments args) { - HandleScope scope; - ASSERT(args.length() == 1); - if (!args[0]->IsJSObject()) { - return Heap::undefined_value(); - } - CONVERT_ARG_CHECKED(JSObject, obj, 0); - - // Skip the global proxy as it has no properties and always delegates to the - // real global object. - if (obj->IsJSGlobalProxy()) { - obj = Handle(JSObject::cast(obj->GetPrototype())); - } - - // Find the number of objects making up this. - int length = LocalPrototypeChainLength(*obj); - - // Find the number of local properties for each of the objects. - int* local_property_count = NewArray(length); - int total_property_count = 0; - Handle jsproto = obj; - for (int i = 0; i < length; i++) { - int n; - n = jsproto->NumberOfLocalProperties(static_cast(NONE)); - local_property_count[i] = n; - total_property_count += n; - if (i < length - 1) { - jsproto = Handle(JSObject::cast(jsproto->GetPrototype())); - } - } - - // Allocate an array with storage for all the property names. - Handle names = Factory::NewFixedArray(total_property_count); - - // Get the property names. - jsproto = obj; - int proto_with_hidden_properties = 0; - for (int i = 0; i < length; i++) { - jsproto->GetLocalPropertyNames(*names, - i == 0 ? 0 : local_property_count[i - 1]); - if (!GetHiddenProperties(jsproto, false)->IsUndefined()) { - proto_with_hidden_properties++; - } - if (i < length - 1) { - jsproto = Handle(JSObject::cast(jsproto->GetPrototype())); - } - } - - // Filter out name of hidden propeties object. - if (proto_with_hidden_properties > 0) { - Handle old_names = names; - names = Factory::NewFixedArray( - names->length() - proto_with_hidden_properties); - int dest_pos = 0; - for (int i = 0; i < total_property_count; i++) { - Object* name = old_names->get(i); - if (name == Heap::hidden_symbol()) { - continue; - } - names->set(dest_pos++, name); - } - } - - DeleteArray(local_property_count); - return *Factory::NewJSArrayWithElements(names); -} - - -// Return the names of the local indexed properties. -// args[0]: object -static Object* Runtime_DebugLocalElementNames(Arguments args) { - HandleScope scope; - ASSERT(args.length() == 1); - if (!args[0]->IsJSObject()) { - return Heap::undefined_value(); - } - CONVERT_ARG_CHECKED(JSObject, obj, 0); - - int n = obj->NumberOfLocalElements(static_cast(NONE)); - Handle names = Factory::NewFixedArray(n); - obj->GetLocalElementKeys(*names, static_cast(NONE)); - return *Factory::NewJSArrayWithElements(names); -} - - // Return the property type calculated from the property details. // args[0]: smi with property details. static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) { @@ -6306,54 +6354,6 @@ static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) { } -// Return information on whether an object has a named or indexed interceptor. -// args[0]: object -static Object* Runtime_DebugInterceptorInfo(Arguments args) { - HandleScope scope; - ASSERT(args.length() == 1); - if (!args[0]->IsJSObject()) { - return Smi::FromInt(0); - } - CONVERT_ARG_CHECKED(JSObject, obj, 0); - - int result = 0; - if (obj->HasNamedInterceptor()) result |= 2; - if (obj->HasIndexedInterceptor()) result |= 1; - - return Smi::FromInt(result); -} - - -// Return property names from named interceptor. -// args[0]: object -static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) { - HandleScope scope; - ASSERT(args.length() == 1); - CONVERT_ARG_CHECKED(JSObject, obj, 0); - - if (obj->HasNamedInterceptor()) { - v8::Handle result = GetKeysForNamedInterceptor(obj, obj); - if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result); - } - return Heap::undefined_value(); -} - - -// Return element names from indexed interceptor. -// args[0]: object -static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) { - HandleScope scope; - ASSERT(args.length() == 1); - CONVERT_ARG_CHECKED(JSObject, obj, 0); - - if (obj->HasIndexedInterceptor()) { - v8::Handle result = GetKeysForIndexedInterceptor(obj, obj); - if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result); - } - return Heap::undefined_value(); -} - - // Return property value from named interceptor. // args[0]: object // args[1]: property name diff --git a/src/runtime.h b/src/runtime.h index b6542a6..59b5f30 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -52,6 +52,11 @@ namespace internal { F(IsPropertyEnumerable, 2, 1) \ F(GetPropertyNames, 1, 1) \ F(GetPropertyNamesFast, 1, 1) \ + F(GetLocalPropertyNames, 1, 1) \ + F(GetLocalElementNames, 1, 1) \ + F(GetInterceptorInfo, 1, 1) \ + F(GetNamedInterceptorPropertyNames, 1, 1) \ + F(GetIndexedInterceptorElementNames, 1, 1) \ F(GetArgumentsProperty, 1, 1) \ F(ToFastProperties, 1, 1) \ F(ToSlowProperties, 1, 1) \ @@ -285,14 +290,9 @@ namespace internal { F(Break, 0, 1) \ F(DebugGetPropertyDetails, 2, 1) \ F(DebugGetProperty, 2, 1) \ - F(DebugLocalPropertyNames, 1, 1) \ - F(DebugLocalElementNames, 1, 1) \ F(DebugPropertyTypeFromDetails, 1, 1) \ F(DebugPropertyAttributesFromDetails, 1, 1) \ F(DebugPropertyIndexFromDetails, 1, 1) \ - F(DebugInterceptorInfo, 1, 1) \ - F(DebugNamedInterceptorPropertyNames, 1, 1) \ - F(DebugIndexedInterceptorElementNames, 1, 1) \ F(DebugNamedInterceptorPropertyValue, 2, 1) \ F(DebugIndexedInterceptorElementValue, 2, 1) \ F(CheckExecutionState, 1, 1) \ diff --git a/src/v8natives.js b/src/v8natives.js index 700b9e4..3dcf430 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -276,7 +276,7 @@ function ObjectLookupSetter(name) { function ObjectKeys(obj) { if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj)) - throw MakeTypeError('object_keys_non_object', [obj]); + throw MakeTypeError("obj_ctor_property_non_object", ["keys"]); return %LocalKeys(obj); } @@ -493,23 +493,59 @@ function DefineOwnProperty(obj, p, desc, should_throw) { // ES5 section 15.2.3.2. function ObjectGetPrototypeOf(obj) { - if (!IS_OBJECT(obj) && !IS_FUNCTION(obj)) { - throw MakeTypeError("object_get_prototype_non_object", [obj]); - } - return obj.__proto__; + if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj)) + throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]); + return obj.__proto__; } // ES5 section 15.2.3.3 function ObjectGetOwnPropertyDescriptor(obj, p) { - if (!IS_OBJECT(obj) && !IS_FUNCTION(obj)) { - throw MakeTypeError("object_get_prototype_non_object", [obj]); - } + if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj)) + throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyDescriptor"]); var desc = GetOwnProperty(obj, p); return FromPropertyDescriptor(desc); } +// ES5 section 15.2.3.4. +function ObjectGetOwnPropertyNames(obj) { + if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj)) + throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]); + + // Find all the indexed properties. + + // Get the local element names. + var propertyNames = %GetLocalElementNames(obj); + + // Get names for indexed interceptor properties. + if (%GetInterceptorInfo(obj) & 1) { + var indexedInterceptorNames = + %GetIndexedInterceptorElementNames(obj); + if (indexedInterceptorNames) { + propertyNames = propertyNames.concat(indexedInterceptorNames); + } + } + + // Find all the named properties. + + // Get the local property names. + propertyNames = propertyNames.concat(%GetLocalPropertyNames(obj)); + + // Get names for named interceptor properties if any. + + if (%GetInterceptorInfo(obj) & 2) { + var namedInterceptorNames = + %GetNamedInterceptorPropertyNames(obj); + if (namedInterceptorNames) { + propertyNames = propertyNames.concat(namedInterceptorNames); + } + } + + return propertyNames; +} + + // ES5 section 15.2.3.5. function ObjectCreate(proto, properties) { if (!IS_OBJECT(proto) && !IS_NULL(proto)) { @@ -576,7 +612,8 @@ function SetupObject() { "keys", ObjectKeys, "create", ObjectCreate, "getPrototypeOf", ObjectGetPrototypeOf, - "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor + "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor, + "getOwnPropertyNames", ObjectGetOwnPropertyNames )); } diff --git a/test/mjsunit/object-get-own-property-names.js b/test/mjsunit/object-get-own-property-names.js new file mode 100644 index 0000000..f52cee2 --- /dev/null +++ b/test/mjsunit/object-get-own-property-names.js @@ -0,0 +1,104 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// 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. + +// Test ES5 section 15.2.3.4 Object.getOwnPropertyNames. + +// Check simple cases. +var obj = { a: 1, b: 2}; +var propertyNames = Object.getOwnPropertyNames(obj); +propertyNames.sort(); +assertEquals(2, propertyNames.length); +assertEquals("a", propertyNames[0]); +assertEquals("b", propertyNames[1]); + +var obj = { a: function(){}, b: function(){} }; +var propertyNames = Object.getOwnPropertyNames(obj); +propertyNames.sort(); +assertEquals(2, propertyNames.length); +assertEquals("a", propertyNames[0]); +assertEquals("b", propertyNames[1]); + +// Check slow case +var obj = { a: 1, b: 2, c: 3 }; +delete obj.b; +var propertyNames = Object.getOwnPropertyNames(obj) +propertyNames.sort(); +assertEquals(2, propertyNames.length); +assertEquals("a", propertyNames[0]); +assertEquals("c", propertyNames[1]); + +// Check that non-enumerable properties are being returned. +var propertyNames = Object.getOwnPropertyNames([1, 2]); +propertyNames.sort(); +assertEquals(3, propertyNames.length); +assertEquals("0", propertyNames[0]); +assertEquals("1", propertyNames[1]); +assertEquals("length", propertyNames[2]); + +// Check that no proto properties are returned. +var obj = { foo: "foo" }; +obj.__proto__ = { bar: "bar" }; +propertyNames = Object.getOwnPropertyNames(obj); +propertyNames.sort(); +assertEquals(1, propertyNames.length); +assertEquals("foo", propertyNames[0]); + +// Check that getter properties are returned. +var obj = {}; +obj.__defineGetter__("getter", function() {}); +propertyNames = Object.getOwnPropertyNames(obj); +propertyNames.sort(); +assertEquals(1, propertyNames.length); +assertEquals("getter", propertyNames[0]); + +try { + Object.getOwnPropertyNames(4); + assertTrue(false); +} catch (e) { + assertTrue(/on non-object/.test(e)); +} + +try { + Object.getOwnPropertyNames("foo"); + assertTrue(false); +} catch (e) { + assertTrue(/on non-object/.test(e)); +} + +try { + Object.getOwnPropertyNames(undefined); + assertTrue(false); +} catch (e) { + assertTrue(/on non-object/.test(e)); +} + +try { + Object.getOwnPropertyNames(null); + assertTrue(false); +} catch (e) { + assertTrue(/on non-object/.test(e)); +} diff --git a/test/mjsunit/regress/regress-crbug-3184.js b/test/mjsunit/regress/regress-crbug-3184.js index fe6cfed..6a851ee 100644 --- a/test/mjsunit/regress/regress-crbug-3184.js +++ b/test/mjsunit/regress/regress-crbug-3184.js @@ -1,3 +1,30 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// 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. + Object.extend = function (dest, source) { for (property in source) dest[property] = source[property]; return dest; -- 2.7.4