eca5b5d7ab
* Hash code is now just done with a private own symbol instead of the hidden string, which predates symbols. * In the long run we should do all hidden properties this way and get rid of the hidden magic 0-length string with the zero hash code. The advantages include less complexity and being able to do things from JS in a natural way. * Initially, the performance of weak set regressed, because it's a little harder to do the lookup in C++. Instead of heroics in C++ to make things faster I moved some functionality into JS and got the performance back. JS is supposed to be good at looking up named properties on objects. * This also changes hash codes of Smis so that they are always Smis. Performance figures are in the comments to the code review. Summary: Most of js-perf-test/Collections is neutral. Set and Map with object keys are 40-50% better. WeakMap is -5% and WeakSet is +9%. After the measurements, I fixed global proxies, which cost 1% on most tests and 5% on the weak ones :-(. In the code review comments is a patch with an example of the heroics we could do in C++ to make lookup faster (I hope we don't have to do this. Instead of checking for the property, then doing a new lookup to insert it, we could do one lookup and handle the addition immediately). With the current benchmarks above this buys us nothing, but if we go back to doing more lookups in C++ instead of in stubs and JS then it's a win. In a similar vein we could give the magic zero hash code to the hash code symbol. Then when we look up the hash code we would sometimes see the table with all the hidden properties. This dual use of the field for either the hash code or the table with all hidden properties and the hash code is rather ugly, and this CL gets rid of it. I'd be loath to bring it back. On the benchmarks quoted above it's slightly slower than moving the hash code lookup to JS like in this CL. One worry is that the benchmark results above are more monomorphic than real world code, so may be overstating the performance benefits of moving to JS. I think this is part of a general issue we have with handling polymorphic code in JS and any solutions there will benefit this solution, which boils down to regular property access. Any improvement there will lift all boats. R=adamk@chromium.org, verwaest@chromium.org BUG= Review URL: https://codereview.chromium.org/1149863005 Cr-Commit-Position: refs/heads/master@{#28622}
178 lines
5.0 KiB
JavaScript
178 lines
5.0 KiB
JavaScript
// Copyright 2012 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.
|
|
|
|
(function(global, utils) {
|
|
|
|
"use strict";
|
|
|
|
%CheckIsBootstrapping();
|
|
|
|
var GlobalObject = global.Object;
|
|
var GlobalWeakMap = global.WeakMap;
|
|
var GlobalWeakSet = global.WeakSet;
|
|
|
|
// -------------------------------------------------------------------
|
|
// Harmony WeakMap
|
|
|
|
function WeakMapConstructor(iterable) {
|
|
if (!%_IsConstructCall()) {
|
|
throw MakeTypeError(kConstructorNotFunction, "WeakMap");
|
|
}
|
|
|
|
%WeakCollectionInitialize(this);
|
|
|
|
if (!IS_NULL_OR_UNDEFINED(iterable)) {
|
|
var adder = this.set;
|
|
if (!IS_SPEC_FUNCTION(adder)) {
|
|
throw MakeTypeError(kPropertyNotFunction, 'set', this);
|
|
}
|
|
for (var nextItem of iterable) {
|
|
if (!IS_SPEC_OBJECT(nextItem)) {
|
|
throw MakeTypeError(kIteratorValueNotAnObject, nextItem);
|
|
}
|
|
%_CallFunction(this, nextItem[0], nextItem[1], adder);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function WeakMapGet(key) {
|
|
if (!IS_WEAKMAP(this)) {
|
|
throw MakeTypeError(kIncompatibleMethodReceiver,
|
|
'WeakMap.prototype.get', this);
|
|
}
|
|
if (!IS_SPEC_OBJECT(key)) return UNDEFINED;
|
|
var hash = $getExistingHash(key);
|
|
if (IS_UNDEFINED(hash)) return UNDEFINED;
|
|
return %WeakCollectionGet(this, key, hash);
|
|
}
|
|
|
|
|
|
function WeakMapSet(key, value) {
|
|
if (!IS_WEAKMAP(this)) {
|
|
throw MakeTypeError(kIncompatibleMethodReceiver,
|
|
'WeakMap.prototype.set', this);
|
|
}
|
|
if (!IS_SPEC_OBJECT(key)) throw MakeTypeError(kInvalidWeakMapKey);
|
|
return %WeakCollectionSet(this, key, value, $getHash(key));
|
|
}
|
|
|
|
|
|
function WeakMapHas(key) {
|
|
if (!IS_WEAKMAP(this)) {
|
|
throw MakeTypeError(kIncompatibleMethodReceiver,
|
|
'WeakMap.prototype.has', this);
|
|
}
|
|
if (!IS_SPEC_OBJECT(key)) return false;
|
|
var hash = $getExistingHash(key);
|
|
if (IS_UNDEFINED(hash)) return false;
|
|
return %WeakCollectionHas(this, key, hash);
|
|
}
|
|
|
|
|
|
function WeakMapDelete(key) {
|
|
if (!IS_WEAKMAP(this)) {
|
|
throw MakeTypeError(kIncompatibleMethodReceiver,
|
|
'WeakMap.prototype.delete', this);
|
|
}
|
|
if (!IS_SPEC_OBJECT(key)) return false;
|
|
var hash = $getExistingHash(key);
|
|
if (IS_UNDEFINED(hash)) return false;
|
|
return %WeakCollectionDelete(this, key, hash);
|
|
}
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
%SetCode(GlobalWeakMap, WeakMapConstructor);
|
|
%FunctionSetLength(GlobalWeakMap, 0);
|
|
%FunctionSetPrototype(GlobalWeakMap, new GlobalObject());
|
|
%AddNamedProperty(GlobalWeakMap.prototype, "constructor", GlobalWeakMap,
|
|
DONT_ENUM);
|
|
%AddNamedProperty(GlobalWeakMap.prototype, symbolToStringTag, "WeakMap",
|
|
DONT_ENUM | READ_ONLY);
|
|
|
|
// Set up the non-enumerable functions on the WeakMap prototype object.
|
|
utils.InstallFunctions(GlobalWeakMap.prototype, DONT_ENUM, [
|
|
"get", WeakMapGet,
|
|
"set", WeakMapSet,
|
|
"has", WeakMapHas,
|
|
"delete", WeakMapDelete
|
|
]);
|
|
|
|
// -------------------------------------------------------------------
|
|
// Harmony WeakSet
|
|
|
|
function WeakSetConstructor(iterable) {
|
|
if (!%_IsConstructCall()) {
|
|
throw MakeTypeError(kConstructorNotFunction, "WeakSet");
|
|
}
|
|
|
|
%WeakCollectionInitialize(this);
|
|
|
|
if (!IS_NULL_OR_UNDEFINED(iterable)) {
|
|
var adder = this.add;
|
|
if (!IS_SPEC_FUNCTION(adder)) {
|
|
throw MakeTypeError(kPropertyNotFunction, 'add', this);
|
|
}
|
|
for (var value of iterable) {
|
|
%_CallFunction(this, value, adder);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function WeakSetAdd(value) {
|
|
if (!IS_WEAKSET(this)) {
|
|
throw MakeTypeError(kIncompatibleMethodReceiver,
|
|
'WeakSet.prototype.add', this);
|
|
}
|
|
if (!IS_SPEC_OBJECT(value)) throw MakeTypeError(kInvalidWeakSetValue);
|
|
return %WeakCollectionSet(this, value, true, $getHash(value));
|
|
}
|
|
|
|
|
|
function WeakSetHas(value) {
|
|
if (!IS_WEAKSET(this)) {
|
|
throw MakeTypeError(kIncompatibleMethodReceiver,
|
|
'WeakSet.prototype.has', this);
|
|
}
|
|
if (!IS_SPEC_OBJECT(value)) return false;
|
|
var hash = $getExistingHash(value);
|
|
if (IS_UNDEFINED(hash)) return false;
|
|
return %WeakCollectionHas(this, value, hash);
|
|
}
|
|
|
|
|
|
function WeakSetDelete(value) {
|
|
if (!IS_WEAKSET(this)) {
|
|
throw MakeTypeError(kIncompatibleMethodReceiver,
|
|
'WeakSet.prototype.delete', this);
|
|
}
|
|
if (!IS_SPEC_OBJECT(value)) return false;
|
|
var hash = $getExistingHash(value);
|
|
if (IS_UNDEFINED(hash)) return false;
|
|
return %WeakCollectionDelete(this, value, hash);
|
|
}
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
%SetCode(GlobalWeakSet, WeakSetConstructor);
|
|
%FunctionSetLength(GlobalWeakSet, 0);
|
|
%FunctionSetPrototype(GlobalWeakSet, new GlobalObject());
|
|
%AddNamedProperty(GlobalWeakSet.prototype, "constructor", GlobalWeakSet,
|
|
DONT_ENUM);
|
|
%AddNamedProperty(GlobalWeakSet.prototype, symbolToStringTag, "WeakSet",
|
|
DONT_ENUM | READ_ONLY);
|
|
|
|
// Set up the non-enumerable functions on the WeakSet prototype object.
|
|
utils.InstallFunctions(GlobalWeakSet.prototype, DONT_ENUM, [
|
|
"add", WeakSetAdd,
|
|
"has", WeakSetHas,
|
|
"delete", WeakSetDelete
|
|
]);
|
|
|
|
})
|