mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 04:32:44 +00:00 
			
		
		
		
	LibJS: Implement WeakMap changes from 'Symbol as WeakMap Keys Proposal'
This commit is contained in:
		
							parent
							
								
									22a78e8a2c
								
							
						
					
					
						commit
						a80d3fdf49
					
				
					 8 changed files with 39 additions and 18 deletions
				
			
		|  | @ -68,10 +68,10 @@ TESTJS_GLOBAL_FUNCTION(mark_as_garbage, markAsGarbage) | ||||||
| 
 | 
 | ||||||
|     auto value = TRY(reference.get_value(global_object)); |     auto value = TRY(reference.get_value(global_object)); | ||||||
| 
 | 
 | ||||||
|     if (!value.is_object()) |     if (!can_be_held_weakly(value)) | ||||||
|         return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAnObject, String::formatted("Variable with name {}", variable_name.string())); |         return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::CannotBeHeldWeakly, String::formatted("Variable with name {}", variable_name.string())); | ||||||
| 
 | 
 | ||||||
|     vm.heap().uproot_cell(&value.as_object()); |     vm.heap().uproot_cell(&value.as_cell()); | ||||||
|     TRY(reference.delete_(global_object)); |     TRY(reference.delete_(global_object)); | ||||||
| 
 | 
 | ||||||
|     return JS::js_undefined(); |     return JS::js_undefined(); | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ | ||||||
|     M(CallStackSizeExceeded, "Call stack size limit exceeded")                                                                          \ |     M(CallStackSizeExceeded, "Call stack size limit exceeded")                                                                          \ | ||||||
|     M(CannotDeclareGlobalFunction, "Cannot declare global function of name '{}'")                                                       \ |     M(CannotDeclareGlobalFunction, "Cannot declare global function of name '{}'")                                                       \ | ||||||
|     M(CannotDeclareGlobalVariable, "Cannot declare global variable of name '{}'")                                                       \ |     M(CannotDeclareGlobalVariable, "Cannot declare global variable of name '{}'")                                                       \ | ||||||
|  |     M(CannotBeHeldWeakly, "{} cannot be held weakly")                                                                                   \ | ||||||
|     M(ClassConstructorWithoutNew, "Class constructor {} must be called with 'new'")                                                     \ |     M(ClassConstructorWithoutNew, "Class constructor {} must be called with 'new'")                                                     \ | ||||||
|     M(ClassExtendsValueNotAConstructorOrNull, "Class extends value {} is not a constructor or null")                                    \ |     M(ClassExtendsValueNotAConstructorOrNull, "Class extends value {} is not a constructor or null")                                    \ | ||||||
|     M(ClassExtendsValueInvalidPrototype, "Class extends value has an invalid prototype {}")                                             \ |     M(ClassExtendsValueInvalidPrototype, "Class extends value has an invalid prototype {}")                                             \ | ||||||
|  |  | ||||||
|  | @ -1,11 +1,12 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> |  * Copyright (c) 2021-2022, Idan Horowitz <idan.horowitz@serenityos.org> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <AK/HashTable.h> | #include <AK/HashTable.h> | ||||||
| #include <AK/TypeCasts.h> | #include <AK/TypeCasts.h> | ||||||
|  | #include <LibJS/Runtime/AbstractOperations.h> | ||||||
| #include <LibJS/Runtime/WeakMapPrototype.h> | #include <LibJS/Runtime/WeakMapPrototype.h> | ||||||
| 
 | 
 | ||||||
| namespace JS { | namespace JS { | ||||||
|  | @ -35,9 +36,9 @@ JS_DEFINE_NATIVE_FUNCTION(WeakMapPrototype::delete_) | ||||||
| { | { | ||||||
|     auto* weak_map = TRY(typed_this_object(global_object)); |     auto* weak_map = TRY(typed_this_object(global_object)); | ||||||
|     auto value = vm.argument(0); |     auto value = vm.argument(0); | ||||||
|     if (!value.is_object()) |     if (!can_be_held_weakly(value)) | ||||||
|         return Value(false); |         return Value(false); | ||||||
|     return Value(weak_map->values().remove(&value.as_object())); |     return Value(weak_map->values().remove(&value.as_cell())); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // 24.3.3.3 WeakMap.prototype.get ( key ), https://tc39.es/ecma262/#sec-weakmap.prototype.get
 | // 24.3.3.3 WeakMap.prototype.get ( key ), https://tc39.es/ecma262/#sec-weakmap.prototype.get
 | ||||||
|  | @ -45,10 +46,10 @@ JS_DEFINE_NATIVE_FUNCTION(WeakMapPrototype::get) | ||||||
| { | { | ||||||
|     auto* weak_map = TRY(typed_this_object(global_object)); |     auto* weak_map = TRY(typed_this_object(global_object)); | ||||||
|     auto value = vm.argument(0); |     auto value = vm.argument(0); | ||||||
|     if (!value.is_object()) |     if (!can_be_held_weakly(value)) | ||||||
|         return js_undefined(); |         return js_undefined(); | ||||||
|     auto& values = weak_map->values(); |     auto& values = weak_map->values(); | ||||||
|     auto result = values.find(&value.as_object()); |     auto result = values.find(&value.as_cell()); | ||||||
|     if (result == values.end()) |     if (result == values.end()) | ||||||
|         return js_undefined(); |         return js_undefined(); | ||||||
|     return result->value; |     return result->value; | ||||||
|  | @ -59,10 +60,10 @@ JS_DEFINE_NATIVE_FUNCTION(WeakMapPrototype::has) | ||||||
| { | { | ||||||
|     auto* weak_map = TRY(typed_this_object(global_object)); |     auto* weak_map = TRY(typed_this_object(global_object)); | ||||||
|     auto value = vm.argument(0); |     auto value = vm.argument(0); | ||||||
|     if (!value.is_object()) |     if (!can_be_held_weakly(value)) | ||||||
|         return Value(false); |         return Value(false); | ||||||
|     auto& values = weak_map->values(); |     auto& values = weak_map->values(); | ||||||
|     return Value(values.find(&value.as_object()) != values.end()); |     return Value(values.find(&value.as_cell()) != values.end()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // 24.3.3.5 WeakMap.prototype.set ( key, value ), https://tc39.es/ecma262/#sec-weakmap.prototype.set
 | // 24.3.3.5 WeakMap.prototype.set ( key, value ), https://tc39.es/ecma262/#sec-weakmap.prototype.set
 | ||||||
|  | @ -70,9 +71,9 @@ JS_DEFINE_NATIVE_FUNCTION(WeakMapPrototype::set) | ||||||
| { | { | ||||||
|     auto* weak_map = TRY(typed_this_object(global_object)); |     auto* weak_map = TRY(typed_this_object(global_object)); | ||||||
|     auto value = vm.argument(0); |     auto value = vm.argument(0); | ||||||
|     if (!value.is_object()) |     if (!can_be_held_weakly(value)) | ||||||
|         return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, value.to_string_without_side_effects()); |         return vm.throw_completion<TypeError>(global_object, ErrorType::CannotBeHeldWeakly, value.to_string_without_side_effects()); | ||||||
|     weak_map->values().set(&value.as_object(), vm.argument(1)); |     weak_map->values().set(&value.as_cell(), vm.argument(1)); | ||||||
|     return weak_map; |     return weak_map; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -37,6 +37,6 @@ describe("regressions", () => { | ||||||
|     test("missing key/value properties on iterable entry", () => { |     test("missing key/value properties on iterable entry", () => { | ||||||
|         expect(() => { |         expect(() => { | ||||||
|             new WeakMap([{}]); |             new WeakMap([{}]); | ||||||
|         }).toThrowWithMessage(TypeError, "undefined is not an object"); |         }).toThrowWithMessage(TypeError, "undefined cannot be held weakly"); | ||||||
|     }); |     }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -5,9 +5,12 @@ test("basic functionality", () => { | ||||||
|         [{ a: 1 }, 1], |         [{ a: 1 }, 1], | ||||||
|         [{ a: 2 }, 2], |         [{ a: 2 }, 2], | ||||||
|         [{ a: 3 }, 3], |         [{ a: 3 }, 3], | ||||||
|  |         [Symbol("foo"), "bar"], | ||||||
|     ]; |     ]; | ||||||
|     const weakMap = new WeakMap(original); |     const weakMap = new WeakMap(original); | ||||||
|     expect(weakMap.delete(original[0][0])).toBeTrue(); |     expect(weakMap.delete(original[0][0])).toBeTrue(); | ||||||
|     expect(weakMap.delete(original[0][0])).toBeFalse(); |     expect(weakMap.delete(original[0][0])).toBeFalse(); | ||||||
|  |     expect(weakMap.delete(original[3][0])).toBeTrue(); | ||||||
|  |     expect(weakMap.delete(original[3][0])).toBeFalse(); | ||||||
|     expect(weakMap.delete(null)).toBeFalse(); |     expect(weakMap.delete(null)).toBeFalse(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -5,8 +5,10 @@ test("basic functionality", () => { | ||||||
|         [{ a: 1 }, 1], |         [{ a: 1 }, 1], | ||||||
|         [{ a: 2 }, 2], |         [{ a: 2 }, 2], | ||||||
|         [{ a: 3 }, 3], |         [{ a: 3 }, 3], | ||||||
|  |         [Symbol("foo"), "bar"], | ||||||
|     ]; |     ]; | ||||||
|     const weakMap = new WeakMap(original); |     const weakMap = new WeakMap(original); | ||||||
|     expect(weakMap.get(original[0][0])).toBe(original[0][1]); |     expect(weakMap.get(original[0][0])).toBe(original[0][1]); | ||||||
|  |     expect(weakMap.get(original[3][0])).toBe(original[3][1]); | ||||||
|     expect(weakMap.get(null)).toBe(undefined); |     expect(weakMap.get(null)).toBe(undefined); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -7,10 +7,12 @@ test("basic functionality", () => { | ||||||
|         [{ a: 1 }, 1], |         [{ a: 1 }, 1], | ||||||
|         [{ a: 2 }, 2], |         [{ a: 2 }, 2], | ||||||
|         [{ a: 3 }, 3], |         [{ a: 3 }, 3], | ||||||
|  |         [Symbol("foo"), "bar"], | ||||||
|     ]; |     ]; | ||||||
|     var weakMap = new WeakMap(original); |     var weakMap = new WeakMap(original); | ||||||
| 
 | 
 | ||||||
|     expect(new WeakMap().has()).toBeFalse(); |     expect(new WeakMap().has()).toBeFalse(); | ||||||
|     expect(weakMap.has(original[0][0])).toBeTrue(); |     expect(weakMap.has(original[0][0])).toBeTrue(); | ||||||
|  |     expect(weakMap.has(original[3][0])).toBeTrue(); | ||||||
|     expect(weakMap.has({ a: 1 })).toBeFalse(); |     expect(weakMap.has({ a: 1 })).toBeFalse(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -5,9 +5,11 @@ test("basic functionality", () => { | ||||||
|         [{ a: 1 }, 1], |         [{ a: 1 }, 1], | ||||||
|         [{ a: 2 }, 2], |         [{ a: 2 }, 2], | ||||||
|         [{ a: 3 }, 3], |         [{ a: 3 }, 3], | ||||||
|  |         [Symbol("foo"), "bar"], | ||||||
|     ]); |     ]); | ||||||
|     expect(weakMap.set({ a: 4 }, 4)).toBe(weakMap); |     expect(weakMap.set({ a: 4 }, 4)).toBe(weakMap); | ||||||
|     expect(weakMap.set({ a: 1 }, 2)).toBe(weakMap); |     expect(weakMap.set({ a: 1 }, 2)).toBe(weakMap); | ||||||
|  |     expect(weakMap.set(Symbol("hello"), "friends")).toBe(weakMap); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test("invalid values", () => { | test("invalid values", () => { | ||||||
|  | @ -15,18 +17,28 @@ test("invalid values", () => { | ||||||
|     [-100, Infinity, NaN, "hello", 152n].forEach(value => { |     [-100, Infinity, NaN, "hello", 152n].forEach(value => { | ||||||
|         expect(() => { |         expect(() => { | ||||||
|             weakMap.set(value, value); |             weakMap.set(value, value); | ||||||
|         }).toThrowWithMessage(TypeError, "is not an object"); |         }).toThrowWithMessage(TypeError, "cannot be held weakly"); | ||||||
|     }); |     }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test("automatic removal of garbage-collected values", () => { | test("automatic removal of garbage-collected values", () => { | ||||||
|     const weakMap = new WeakMap(); |     const weakMap = new WeakMap(); | ||||||
|     const key = { e: 3 }; |     const objectKey = { e: 3 }; | ||||||
| 
 | 
 | ||||||
|     expect(weakMap.set(key, 1)).toBe(weakMap); |     expect(weakMap.set(objectKey, 1)).toBe(weakMap); | ||||||
|     expect(getWeakMapSize(weakMap)).toBe(1); |     expect(getWeakMapSize(weakMap)).toBe(1); | ||||||
| 
 | 
 | ||||||
|     markAsGarbage("key"); |     markAsGarbage("objectKey"); | ||||||
|  |     gc(); | ||||||
|  | 
 | ||||||
|  |     expect(getWeakMapSize(weakMap)).toBe(0); | ||||||
|  | 
 | ||||||
|  |     const symbolKey = Symbol("foo"); | ||||||
|  | 
 | ||||||
|  |     expect(weakMap.set(symbolKey, "bar")).toBe(weakMap); | ||||||
|  |     expect(getWeakMapSize(weakMap)).toBe(1); | ||||||
|  | 
 | ||||||
|  |     markAsGarbage("symbolKey"); | ||||||
|     gc(); |     gc(); | ||||||
| 
 | 
 | ||||||
|     expect(getWeakMapSize(weakMap)).toBe(0); |     expect(getWeakMapSize(weakMap)).toBe(0); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Idan Horowitz
						Idan Horowitz