1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.objects; 27 28 import java.util.Map; 29 import java.util.WeakHashMap; 30 import jdk.nashorn.internal.objects.annotations.Attribute; 31 import jdk.nashorn.internal.objects.annotations.Constructor; 32 import jdk.nashorn.internal.objects.annotations.Function; 33 import jdk.nashorn.internal.objects.annotations.ScriptClass; 34 import jdk.nashorn.internal.runtime.PropertyMap; 35 import jdk.nashorn.internal.runtime.ScriptObject; 36 import jdk.nashorn.internal.runtime.ScriptRuntime; 37 import jdk.nashorn.internal.runtime.Undefined; 38 39 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 40 import static jdk.nashorn.internal.runtime.JSType.isPrimitive; 41 42 /** 43 * This implements the ECMA6 WeakMap object. 44 */ 45 @ScriptClass("WeakMap") 46 public class NativeWeakMap extends ScriptObject { 47 48 private final Map<Object, Object> jmap = new WeakHashMap<>(); 49 50 // initialized by nasgen 51 private static PropertyMap $nasgenmap$; 52 53 private NativeWeakMap(final ScriptObject proto, final PropertyMap map) { 54 super(proto, map); 55 } 56 57 /** 58 * ECMA6 23.3.1 The WeakMap Constructor 59 * 60 * @param isNew whether the new operator used 61 * @param self self reference 62 * @param arg optional iterable argument 63 * @return a new WeakMap object 64 */ 65 @Constructor(arity = 0) 66 public static Object construct(final boolean isNew, final Object self, final Object arg) { 67 if (!isNew) { 68 throw typeError("constructor.requires.new", "WeakMap"); 69 } 70 final Global global = Global.instance(); 71 final NativeWeakMap weakMap = new NativeWeakMap(global.getWeakMapPrototype(), $nasgenmap$); 72 populateMap(weakMap.jmap, arg, global); 73 return weakMap; 74 } 75 76 /** 77 * ECMA6 23.3.3.5 WeakMap.prototype.set ( key , value ) 78 * 79 * @param self the self reference 80 * @param key the key 81 * @param value the value 82 * @return this WeakMap object 83 */ 84 @Function(attributes = Attribute.NOT_ENUMERABLE) 85 public static Object set(final Object self, final Object key, final Object value) { 86 final NativeWeakMap map = getMap(self); 87 map.jmap.put(checkKey(key), value); 88 return self; 89 } 90 91 /** 92 * ECMA6 23.3.3.3 WeakMap.prototype.get ( key ) 93 * 94 * @param self the self reference 95 * @param key the key 96 * @return the associated value or undefined 97 */ 98 @Function(attributes = Attribute.NOT_ENUMERABLE) 99 public static Object get(final Object self, final Object key) { 100 final NativeWeakMap map = getMap(self); 101 if (isPrimitive(key)) { 102 return Undefined.getUndefined(); 103 } 104 return map.jmap.get(key); 105 } 106 107 /** 108 * ECMA6 23.3.3.2 WeakMap.prototype.delete ( key ) 109 * 110 * @param self the self reference 111 * @param key the key to delete 112 * @return true if the key was deleted 113 */ 114 @Function(attributes = Attribute.NOT_ENUMERABLE) 115 public static boolean delete(final Object self, final Object key) { 116 final Map<Object, Object> map = getMap(self).jmap; 117 if (isPrimitive(key)) { 118 return false; 119 } 120 final boolean returnValue = map.containsKey(key); 121 map.remove(key); 122 return returnValue; 123 } 124 125 /** 126 * ECMA6 23.3.3.4 WeakMap.prototype.has ( key ) 127 * 128 * @param self the self reference 129 * @param key the key 130 * @return true if key is contained 131 */ 132 @Function(attributes = Attribute.NOT_ENUMERABLE) 133 public static boolean has(final Object self, final Object key) { 134 final NativeWeakMap map = getMap(self); 135 return !isPrimitive(key) && map.jmap.containsKey(key); 136 } 137 138 @Override 139 public String getClassName() { 140 return "WeakMap"; 141 } 142 143 /** 144 * Make sure {@code key} is not a JavaScript primitive value. 145 * 146 * @param key a key object 147 * @return the valid key 148 */ 149 static Object checkKey(final Object key) { 150 if (isPrimitive(key)) { 151 throw typeError("invalid.weak.key", ScriptRuntime.safeToString(key)); 152 } 153 return key; 154 } 155 156 static void populateMap(final Map<Object, Object> map, final Object arg, final Global global) { 157 // This method is similar to NativeMap.populateMap, but it uses a different 158 // map implementation and the checking/conversion of keys differs as well. 159 if (arg != null && arg != Undefined.getUndefined()) { 160 AbstractIterator.iterate(arg, global, value -> { 161 if (isPrimitive(value)) { 162 throw typeError(global, "not.an.object", ScriptRuntime.safeToString(value)); 163 } 164 if (value instanceof ScriptObject) { 165 final ScriptObject sobj = (ScriptObject) value; 166 map.put(checkKey(sobj.get(0)), sobj.get(1)); 167 } 168 }); 169 } 170 } 171 172 private static NativeWeakMap getMap(final Object self) { 173 if (self instanceof NativeWeakMap) { 174 return (NativeWeakMap)self; 175 } else { 176 throw typeError("not.a.weak.map", ScriptRuntime.safeToString(self)); 177 } 178 } 179 180 } 181 182