From fb70df076ba040f493676291416ffa5e14a873eb Mon Sep 17 00:00:00 2001 From: "adamk@chromium.org" Date: Fri, 9 May 2014 18:22:28 +0000 Subject: [PATCH] Object.observe: avoid accessing acceptList properties more than once BUG=v8:3315 LOG=Y R=rossberg@chromium.org Review URL: https://codereview.chromium.org/270763003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21244 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/object-observe.js | 46 ++++++++++++++++++++---------------- src/runtime.cc | 1 - test/mjsunit/regress/regress-3315.js | 26 ++++++++++++++++++++ 3 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 test/mjsunit/regress/regress-3315.js diff --git a/src/object-observe.js b/src/object-observe.js index 532b0d2..f4b1f53 100644 --- a/src/object-observe.js +++ b/src/object-observe.js @@ -127,9 +127,9 @@ function TypeMapRemoveType(typeMap, type) { typeMap[type]--; } -function TypeMapCreateFromList(typeList) { +function TypeMapCreateFromList(typeList, length) { var typeMap = TypeMapCreate(); - for (var i = 0; i < typeList.length; i++) { + for (var i = 0; i < length; i++) { TypeMapAddType(typeMap, typeList[i], true); } return typeMap; @@ -151,14 +151,17 @@ function TypeMapIsDisjointFrom(typeMap1, typeMap2) { return true; } -var defaultAcceptTypes = TypeMapCreateFromList([ - 'add', - 'update', - 'delete', - 'setPrototype', - 'reconfigure', - 'preventExtensions' -]); +var defaultAcceptTypes = (function() { + var defaultTypes = [ + 'add', + 'update', + 'delete', + 'setPrototype', + 'reconfigure', + 'preventExtensions' + ]; + return TypeMapCreateFromList(defaultTypes, defaultTypes.length); +})(); // An Observer is a registration to observe an object by a callback with // a given set of accept types. If the set of accept types is the default @@ -170,7 +173,7 @@ function ObserverCreate(callback, acceptList) { return callback; var observer = nullProtoObject(); observer.callback = callback; - observer.accept = TypeMapCreateFromList(acceptList); + observer.accept = acceptList; return observer; } @@ -306,16 +309,18 @@ function ObjectInfoGetPerformingTypes(objectInfo) { return objectInfo.performingCount > 0 ? objectInfo.performing : null; } -function AcceptArgIsValid(arg) { +function ConvertAcceptListToTypeMap(arg) { + // We use undefined as a sentinel for the default accept list. if (IS_UNDEFINED(arg)) - return true; + return arg; - if (!IS_SPEC_OBJECT(arg) || - !IS_NUMBER(arg.length) || - arg.length < 0) - return false; + if (!IS_SPEC_OBJECT(arg)) + throw MakeTypeError("observe_accept_invalid"); - return true; + var len = ToInteger(arg.length); + if (len < 0) len = 0; + + return TypeMapCreateFromList(arg, len); } // CallbackInfo's optimized state is just a number which represents its global @@ -362,15 +367,14 @@ function ObjectObserve(object, callback, acceptList) { throw MakeTypeError("observe_non_function", ["observe"]); if (ObjectIsFrozen(callback)) throw MakeTypeError("observe_callback_frozen"); - if (!AcceptArgIsValid(acceptList)) - throw MakeTypeError("observe_accept_invalid"); return %ObjectObserveInObjectContext(object, callback, acceptList); } function NativeObjectObserve(object, callback, acceptList) { var objectInfo = ObjectInfoGetOrCreate(object); - ObjectInfoAddObserver(objectInfo, callback, acceptList); + var typeList = ConvertAcceptListToTypeMap(acceptList); + ObjectInfoAddObserver(objectInfo, callback, typeList); return object; } diff --git a/src/runtime.cc b/src/runtime.cc index 3e8167a..143b870 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -14952,7 +14952,6 @@ RUNTIME_FUNCTION(Runtime_ObjectObserveInObjectContext) { CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); CONVERT_ARG_HANDLE_CHECKED(JSFunction, callback, 1); CONVERT_ARG_HANDLE_CHECKED(Object, accept, 2); - RUNTIME_ASSERT(accept->IsUndefined() || accept->IsJSObject()); Handle context(object->GetCreationContext(), isolate); Handle function(context->native_object_observe(), isolate); diff --git a/test/mjsunit/regress/regress-3315.js b/test/mjsunit/regress/regress-3315.js new file mode 100644 index 0000000..a1105e2 --- /dev/null +++ b/test/mjsunit/regress/regress-3315.js @@ -0,0 +1,26 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var indexZeroCallCount = 0; +var indexOneCallCount = 0; +var lengthCallCount = 0; +var acceptList = { + get 0() { + indexZeroCallCount++; + return 'foo'; + }, + get 1() { + indexOneCallCount++; + return 'bar'; + }, + get length() { + lengthCallCount++; + return 1; + } +}; + +Object.observe({}, function(){}, acceptList); +assertEquals(1, lengthCallCount); +assertEquals(1, indexZeroCallCount); +assertEquals(0, indexOneCallCount); -- 2.7.4