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
This commit is contained in:
mikhail.naganov@gmail.com 2010-01-15 15:34:32 +00:00
parent 4a90166dde
commit a3c0f20035
7 changed files with 339 additions and 172 deletions

View File

@ -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"
};
}

View File

@ -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;

View File

@ -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>(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<int>(length);
int total_property_count = 0;
Handle<JSObject> jsproto = obj;
for (int i = 0; i < length; i++) {
int n;
n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
local_property_count[i] = n;
total_property_count += n;
if (i < length - 1) {
jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
}
}
// Allocate an array with storage for all the property names.
Handle<FixedArray> 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>(JSObject::cast(jsproto->GetPrototype()));
}
}
// Filter out name of hidden propeties object.
if (proto_with_hidden_properties > 0) {
Handle<FixedArray> 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<PropertyAttributes>(NONE));
Handle<FixedArray> names = Factory::NewFixedArray(n);
obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(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<v8::Array> 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<v8::Array> 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>(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<int>(length);
int total_property_count = 0;
Handle<JSObject> jsproto = obj;
for (int i = 0; i < length; i++) {
int n;
n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
local_property_count[i] = n;
total_property_count += n;
if (i < length - 1) {
jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
}
}
// Allocate an array with storage for all the property names.
Handle<FixedArray> 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>(JSObject::cast(jsproto->GetPrototype()));
}
}
// Filter out name of hidden propeties object.
if (proto_with_hidden_properties > 0) {
Handle<FixedArray> 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<PropertyAttributes>(NONE));
Handle<FixedArray> names = Factory::NewFixedArray(n);
obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(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<v8::Array> 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<v8::Array> 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

View File

@ -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) \

View File

@ -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
));
}

View File

@ -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));
}

View File

@ -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;