Preliminary Harmony weak maps API implementation.

R=rossberg@chromium.org,danno@chromium.org
BUG=v8:1565
TEST=mjsunit/harmony/weakmaps

Review URL: http://codereview.chromium.org/7529007

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8811 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent bdf6895b
......@@ -308,6 +308,7 @@ debug-debugger.js
EXPERIMENTAL_LIBRARY_FILES = '''
proxy.js
weakmap.js
'''.split()
......
......@@ -199,6 +199,7 @@ class Genesis BASE_EMBEDDED {
// New context initialization. Used for creating a context from scratch.
void InitializeGlobal(Handle<GlobalObject> inner_global,
Handle<JSFunction> empty_function);
void InitializeExperimentalGlobal();
// Installs the contents of the native .js files on the global objects.
// Used for creating a context from scratch.
void InstallNativeFunctions();
......@@ -1190,6 +1191,21 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
}
void Genesis::InitializeExperimentalGlobal() {
Isolate* isolate = this->isolate();
Handle<JSObject> global = Handle<JSObject>(global_context()->global());
// TODO (mstarzinger): Move this into Genesis::InitializeGlobal once we no
// longer need to live behind a flag, so WeakMap gets added to the snapshot.
if (FLAG_harmony_weakmaps) { // -- W e a k M a p
Handle<JSFunction> weakmap_fun =
InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize,
isolate->initial_object_prototype(),
Builtins::kIllegal, true);
}
}
bool Genesis::CompileBuiltin(Isolate* isolate, int index) {
Vector<const char> name = Natives::GetScriptName(index);
Handle<String> source_code =
......@@ -1680,6 +1696,11 @@ bool Genesis::InstallExperimentalNatives() {
"native proxy.js") == 0) {
if (!CompileExperimentalBuiltin(isolate(), i)) return false;
}
if (FLAG_harmony_weakmaps &&
strcmp(ExperimentalNatives::GetScriptName(i).start(),
"native weakmap.js") == 0) {
if (!CompileExperimentalBuiltin(isolate(), i)) return false;
}
}
InstallExperimentalNativeFunctions();
......@@ -2169,7 +2190,8 @@ Genesis::Genesis(Isolate* isolate,
isolate->counters()->contexts_created_from_scratch()->Increment();
}
// Install experimental natives.
// Initialize experimental globals and install experimental natives.
InitializeExperimentalGlobal();
if (!InstallExperimentalNatives()) return;
result_ = global_context_;
......
......@@ -98,6 +98,7 @@ private:
// Flags for experimental language features.
DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
DEFINE_bool(harmony_weakmaps, false, "enable harmony weak maps")
// Flags for experimental implementation features.
DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles")
......
......@@ -201,6 +201,7 @@ function FormatMessage(message) {
proxy_prop_not_configurable: ["Trap ", "%1", " of proxy handler ", "%0", " returned non-configurable descriptor for property ", "%2"],
proxy_non_object_prop_names: ["Trap ", "%1", " returned non-object ", "%0"],
proxy_repeated_prop_name: ["Trap ", "%1", " returned repeated property name ", "%2"],
invalid_weakmap_key: ["Invalid value used as weak map key"],
// RangeError
invalid_array_length: ["Invalid array length"],
stack_overflow: ["Maximum call stack size exceeded"],
......
......@@ -153,6 +153,9 @@ void HeapObject::HeapObjectVerify() {
case JS_ARRAY_TYPE:
JSArray::cast(this)->JSArrayVerify();
break;
case JS_WEAK_MAP_TYPE:
JSWeakMap::cast(this)->JSWeakMapVerify();
break;
case JS_REGEXP_TYPE:
JSRegExp::cast(this)->JSRegExpVerify();
break;
......@@ -453,6 +456,14 @@ void JSArray::JSArrayVerify() {
}
void JSWeakMap::JSWeakMapVerify() {
CHECK(IsJSWeakMap());
JSObjectVerify();
VerifyHeapPointer(table());
ASSERT(table()->IsHashTable());
}
void JSRegExp::JSRegExpVerify() {
JSObjectVerify();
ASSERT(data()->IsUndefined() || data()->IsFixedArray());
......
......@@ -481,6 +481,12 @@ bool Object::IsJSFunctionProxy() {
}
bool Object::IsJSWeakMap() {
return Object::IsJSObject() &&
HeapObject::cast(this)->map()->instance_type() == JS_WEAK_MAP_TYPE;
}
bool Object::IsJSContextExtensionObject() {
return IsHeapObject()
&& (HeapObject::cast(this)->map()->instance_type() ==
......@@ -1417,6 +1423,8 @@ int JSObject::GetHeaderSize() {
return JSValue::kSize;
case JS_ARRAY_TYPE:
return JSValue::kSize;
case JS_WEAK_MAP_TYPE:
return JSWeakMap::kSize;
case JS_REGEXP_TYPE:
return JSValue::kSize;
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
......@@ -2076,6 +2084,7 @@ CAST_ACCESSOR(JSArray)
CAST_ACCESSOR(JSRegExp)
CAST_ACCESSOR(JSProxy)
CAST_ACCESSOR(JSFunctionProxy)
CAST_ACCESSOR(JSWeakMap)
CAST_ACCESSOR(Foreign)
CAST_ACCESSOR(ByteArray)
CAST_ACCESSOR(ExternalArray)
......@@ -3851,6 +3860,9 @@ ACCESSORS(JSProxy, handler, Object, kHandlerOffset)
ACCESSORS(JSProxy, padding, Object, kPaddingOffset)
ACCESSORS(JSWeakMap, table, ObjectHashTable, kTableOffset)
Address Foreign::address() {
return AddressFrom<Address>(READ_INTPTR_FIELD(this, kAddressOffset));
}
......
......@@ -151,6 +151,9 @@ void HeapObject::HeapObjectPrint(FILE* out) {
case JS_PROXY_TYPE:
JSProxy::cast(this)->JSProxyPrint(out);
break;
case JS_WEAK_MAP_TYPE:
JSWeakMap::cast(this)->JSWeakMapPrint(out);
break;
case FOREIGN_TYPE:
Foreign::cast(this)->ForeignPrint(out);
break;
......@@ -431,6 +434,7 @@ static const char* TypeToString(InstanceType type) {
case CODE_TYPE: return "CODE";
case JS_ARRAY_TYPE: return "JS_ARRAY";
case JS_PROXY_TYPE: return "JS_PROXY";
case JS_WEAK_MAP_TYPE: return "JS_WEAK_MAP";
case JS_REGEXP_TYPE: return "JS_REGEXP";
case JS_VALUE_TYPE: return "JS_VALUE";
case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT";
......@@ -584,6 +588,16 @@ void JSProxy::JSProxyPrint(FILE* out) {
}
void JSWeakMap::JSWeakMapPrint(FILE* out) {
HeapObject::PrintHeader(out, "JSWeakMap");
PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
PrintF(out, " - number of elements = %d\n", table()->NumberOfElements());
PrintF(out, " - table = ");
table()->ShortPrint(out);
PrintF(out, "\n");
}
void JSFunction::JSFunctionPrint(FILE* out) {
HeapObject::PrintHeader(out, "Function");
PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
......
......@@ -115,6 +115,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case JS_GLOBAL_OBJECT_TYPE:
case JS_BUILTINS_OBJECT_TYPE:
case JS_MESSAGE_OBJECT_TYPE:
case JS_WEAK_MAP_TYPE:
return GetVisitorIdForSize(kVisitJSObject,
kVisitJSObjectGeneric,
instance_size);
......
......@@ -963,6 +963,11 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) {
accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
break;
}
case JS_WEAK_MAP_TYPE: {
int elements = JSWeakMap::cast(this)->table()->NumberOfElements();
accumulator->Add("<JS WeakMap[%d]>", elements);
break;
}
case JS_REGEXP_TYPE: {
accumulator->Add("<JS RegExp>");
break;
......@@ -1193,6 +1198,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_VALUE_TYPE:
case JS_ARRAY_TYPE:
case JS_WEAK_MAP_TYPE:
case JS_REGEXP_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
......
......@@ -51,6 +51,7 @@
// - JSReceiver (suitable for property access)
// - JSObject
// - JSArray
// - JSWeakMap
// - JSRegExp
// - JSFunction
// - GlobalObject
......@@ -333,6 +334,7 @@ static const int kVariableSizeSentinel = 0;
V(JS_GLOBAL_PROXY_TYPE) \
V(JS_ARRAY_TYPE) \
V(JS_PROXY_TYPE) \
V(JS_WEAK_MAP_TYPE) \
V(JS_REGEXP_TYPE) \
\
V(JS_FUNCTION_TYPE) \
......@@ -570,6 +572,7 @@ enum InstanceType {
JS_GLOBAL_PROXY_TYPE,
JS_ARRAY_TYPE,
JS_PROXY_TYPE,
JS_WEAK_MAP_TYPE,
JS_REGEXP_TYPE, // LAST_NONCALLABLE_SPEC_OBJECT_TYPE
......@@ -752,6 +755,7 @@ class MaybeObject BASE_EMBEDDED {
V(JSArray) \
V(JSProxy) \
V(JSFunctionProxy) \
V(JSWeakMap) \
V(JSRegExp) \
V(HashTable) \
V(Dictionary) \
......@@ -6633,6 +6637,33 @@ class JSFunctionProxy: public JSProxy {
};
// The JSWeakMap describes EcmaScript Harmony weak maps
class JSWeakMap: public JSObject {
public:
// [table]: the backing hash table mapping keys to values.
DECL_ACCESSORS(table, ObjectHashTable)
// Casting.
static inline JSWeakMap* cast(Object* obj);
#ifdef OBJECT_PRINT
inline void JSWeakMapPrint() {
JSWeakMapPrint(stdout);
}
void JSWeakMapPrint(FILE* out);
#endif
#ifdef DEBUG
void JSWeakMapVerify();
#endif
static const int kTableOffset = JSObject::kHeaderSize;
static const int kSize = kTableOffset + kPointerSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSWeakMap);
};
// Foreign describes objects pointing from JavaScript to C structures.
// Since they cannot contain references to JS HeapObjects they can be
// placed in old_data_space.
......
......@@ -635,6 +635,41 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Fix) {
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapInitialize) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSWeakMap, weakmap, 0);
ASSERT(weakmap->map()->inobject_properties() == 0);
Handle<ObjectHashTable> table = isolate->factory()->NewObjectHashTable(0);
weakmap->set_table(*table);
return *weakmap;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapGet) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
CONVERT_ARG_CHECKED(JSWeakMap, weakmap, 0);
// TODO (mstarzinger): Currently we cannot use JSProxy objects as keys
// because they cannot be cast to JSObject to get an identity hash code.
CONVERT_ARG_CHECKED(JSObject, key, 1);
return weakmap->table()->Lookup(*key);
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapSet) {
HandleScope scope(isolate);
ASSERT(args.length() == 3);
CONVERT_ARG_CHECKED(JSWeakMap, weakmap, 0);
// TODO (mstarzinger): See Runtime_WeakMapGet above.
CONVERT_ARG_CHECKED(JSObject, key, 1);
Handle<Object> value(args[2]);
Handle<ObjectHashTable> table(weakmap->table());
weakmap->set_table(*PutIntoObjectHashTable(table, key, value));
return *value;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
......
......@@ -287,6 +287,11 @@ namespace internal {
F(GetHandler, 1, 1) \
F(Fix, 1, 1) \
\
/* Harmony weakmaps */ \
F(WeakMapInitialize, 1, 1) \
F(WeakMapGet, 2, 1) \
F(WeakMapSet, 3, 1) \
\
/* Statements */ \
F(NewClosure, 3, 1) \
F(NewObject, 1, 1) \
......
// Copyright 2011 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.
// This file relies on the fact that the following declaration has been made
// in runtime.js:
// const $Object = global.Object;
const $WeakMap = global.WeakMap;
// -------------------------------------------------------------------
// Set the WeakMap function and constructor.
%SetCode($WeakMap, function(x) {
if (%_IsConstructCall()) {
%WeakMapInitialize(this);
} else {
return new $WeakMap();
}
});
function WeakMapGet(key) {
if (!IS_SPEC_OBJECT(key)) {
throw %MakeTypeError('invalid_weakmap_key', [this, key]);
}
return %WeakMapGet(this, key);
}
function WeakMapSet(key, value) {
if (!IS_SPEC_OBJECT(key)) {
throw %MakeTypeError('invalid_weakmap_key', [this, key]);
}
return %WeakMapSet(this, key, value);
}
// -------------------------------------------------------------------
function SetupWeakMap() {
// Setup the non-enumerable functions on the WeakMap prototype object.
InstallFunctionsOnHiddenPrototype($WeakMap.prototype, DONT_ENUM, $Array(
"get", WeakMapGet,
"set", WeakMapSet
));
}
SetupWeakMap();
// Copyright 2011 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.
// Flags: --harmony-weakmaps --expose-gc
// Test valid getter and setter calls
var m = new WeakMap;
assertDoesNotThrow(function () { m.get(new Object) });
assertDoesNotThrow(function () { m.set(new Object) });
// Test invalid getter and setter calls
var m = new WeakMap;
assertThrows(function () { m.get(undefined) }, TypeError);
assertThrows(function () { m.set(undefined, 0) }, TypeError);
assertThrows(function () { m.get(0) }, TypeError);
assertThrows(function () { m.set(0, 0) }, TypeError);
assertThrows(function () { m.get('a-key') }, TypeError);
assertThrows(function () { m.set('a-key', 0) }, TypeError);
// Test expected mapping behavior
var m = new WeakMap;
function TestMapping(map, key, value) {
map.set(key, value);
assertSame(value, map.get(key));
}
TestMapping(m, new Object, 23);
TestMapping(m, new Object, 'the-value');
TestMapping(m, new Object, new Object);
// Test GC of map with entry
var m = new WeakMap;
var key = new Object;
m.set(key, 'not-collected');
gc();
assertSame('not-collected', m.get(key));
// Test GC of map with chained entries
var m = new WeakMap;
var head = new Object;
for (key = head, i = 0; i < 10; i++, key = m.get(key)) {
m.set(key, new Object);
}
gc();
var count = 0;
for (key = head; key != undefined; key = m.get(key)) {
count++;
}
assertEquals(11, count);
// Test property attribute [[Enumerable]]
var m = new WeakMap;
function props(x) {
var array = [];
for (var p in x) array.push(p);
return array.sort();
}
assertArrayEquals([], props(WeakMap));
assertArrayEquals([], props(WeakMap.prototype));
assertArrayEquals([], props(m));
// Test arbitrary properties on weak maps
var m = new WeakMap;
function TestProperty(map, property, value) {
map[property] = value;
assertEquals(value, map[property]);
}
for (i = 0; i < 20; i++) {
TestProperty(m, i, 'val' + i);
TestProperty(m, 'foo' + i, 'bar' + i);
}
TestMapping(m, new Object, 'foobar');
// Test direct constructor call
var m = WeakMap();
assertTrue(m instanceof WeakMap);
// Test some common JavaScript idioms
var m = new WeakMap;
assertTrue(m instanceof WeakMap);
assertTrue(WeakMap.prototype.set instanceof Function)
assertTrue(WeakMap.prototype.get instanceof Function)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment