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.lang.invoke.MethodHandle; 29 import jdk.nashorn.internal.objects.annotations.Attribute; 30 import jdk.nashorn.internal.objects.annotations.Constructor; 31 import jdk.nashorn.internal.objects.annotations.Function; 32 import jdk.nashorn.internal.objects.annotations.Getter; 33 import jdk.nashorn.internal.objects.annotations.ScriptClass; 34 import jdk.nashorn.internal.objects.annotations.Where; 35 import jdk.nashorn.internal.runtime.PropertyMap; 36 import jdk.nashorn.internal.runtime.ScriptObject; 37 import jdk.nashorn.internal.runtime.ScriptRuntime; 38 import jdk.nashorn.internal.runtime.Undefined; 39 import jdk.nashorn.internal.runtime.linker.Bootstrap; 40 41 import static jdk.nashorn.internal.objects.NativeMap.convertKey; 42 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 43 44 /** 45 * This implements the ECMA6 Set object. 46 */ 47 @ScriptClass("Set") 48 public class NativeSet extends ScriptObject { 49 50 // our set/map implementation 51 private final LinkedMap map = new LinkedMap(); 52 53 // Invoker for the forEach callback 54 private final static Object FOREACH_INVOKER_KEY = new Object(); 55 56 // initialized by nasgen 57 private static PropertyMap $nasgenmap$; 58 59 private NativeSet(final ScriptObject proto, final PropertyMap map) { 60 super(proto, map); 61 } 62 63 /** 64 * ECMA6 23.1 Set constructor 65 * 66 * @param isNew whether the new operator used 67 * @param self self reference 68 * @param arg optional iterable argument 69 * @return a new Set object 70 */ 71 @Constructor(arity = 0) 72 public static Object construct(final boolean isNew, final Object self, final Object arg){ 73 if (!isNew) { 74 throw typeError("constructor.requires.new", "Set"); 75 } 76 final Global global = Global.instance(); 77 final NativeSet set = new NativeSet(global.getSetPrototype(), $nasgenmap$); 78 populateSet(set.getJavaMap(), arg, global); 79 return set; 80 } 81 82 /** 83 * ECMA6 23.2.3.1 Set.prototype.add ( value ) 84 * 85 * @param self the self reference 86 * @param value the value to add 87 * @return this Set object 88 */ 89 @Function(attributes = Attribute.NOT_ENUMERABLE) 90 public static Object add(final Object self, final Object value) { 91 getNativeSet(self).map.set(convertKey(value), null); 92 return self; 93 } 94 95 /** 96 * ECMA6 23.2.3.7 Set.prototype.has ( value ) 97 * 98 * @param self the self reference 99 * @param value the value 100 * @return true if value is contained 101 */ 102 @Function(attributes = Attribute.NOT_ENUMERABLE) 103 public static boolean has(final Object self, final Object value) { 104 return getNativeSet(self).map.has(convertKey(value)); 105 } 106 107 /** 108 * ECMA6 23.2.3.2 Set.prototype.clear ( ) 109 * 110 * @param self the self reference 111 */ 112 @Function(attributes = Attribute.NOT_ENUMERABLE) 113 public static void clear(final Object self) { 114 getNativeSet(self).map.clear(); 115 } 116 117 /** 118 * ECMA6 23.2.3.4 Set.prototype.delete ( value ) 119 * 120 * @param self the self reference 121 * @param value the value 122 * @return true if value was deleted 123 */ 124 @Function(attributes = Attribute.NOT_ENUMERABLE) 125 public static boolean delete(final Object self, final Object value) { 126 return getNativeSet(self).map.delete(convertKey(value)); 127 } 128 129 /** 130 * ECMA6 23.2.3.9 get Set.prototype.size 131 * 132 * @param self the self reference 133 * @return the number of contained values 134 */ 135 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.IS_ACCESSOR, where = Where.PROTOTYPE) 136 public static int size(final Object self) { 137 return getNativeSet(self).map.size(); 138 } 139 140 /** 141 * ECMA6 23.2.3.5 Set.prototype.entries ( ) 142 * 143 * @param self the self reference 144 * @return an iterator over the Set object's entries 145 */ 146 @Function(attributes = Attribute.NOT_ENUMERABLE) 147 public static Object entries(final Object self) { 148 return new SetIterator(getNativeSet(self), AbstractIterator.IterationKind.KEY_VALUE, Global.instance()); 149 } 150 151 /** 152 * ECMA6 23.2.3.8 Set.prototype.keys ( ) 153 * 154 * @param self the self reference 155 * @return an iterator over the Set object's values 156 */ 157 @Function(attributes = Attribute.NOT_ENUMERABLE) 158 public static Object keys(final Object self) { 159 return new SetIterator(getNativeSet(self), AbstractIterator.IterationKind.KEY, Global.instance()); 160 } 161 162 /** 163 * ECMA6 23.2.3.10 Set.prototype.values ( ) 164 * 165 * @param self the self reference 166 * @return an iterator over the Set object's values 167 */ 168 @Function(attributes = Attribute.NOT_ENUMERABLE) 169 public static Object values(final Object self) { 170 return new SetIterator(getNativeSet(self), AbstractIterator.IterationKind.VALUE, Global.instance()); 171 } 172 173 /** 174 * ECMA6 23.2.3.11 Set.prototype [ @@iterator ] ( ) 175 * 176 * @param self the self reference 177 * @return an iterator over the Set object's values 178 */ 179 @Function(attributes = Attribute.NOT_ENUMERABLE, name = "@@iterator") 180 public static Object getIterator(final Object self) { 181 return new SetIterator(getNativeSet(self), AbstractIterator.IterationKind.VALUE, Global.instance()); 182 } 183 184 /** 185 * ECMA6 23.2.3.6 Set.prototype.forEach ( callbackfn [ , thisArg ] ) 186 * 187 * @param self the self reference 188 * @param callbackFn the callback function 189 * @param thisArg optional this object 190 */ 191 @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) 192 public static void forEach(final Object self, final Object callbackFn, final Object thisArg) { 193 final NativeSet set = getNativeSet(self); 194 if (!Bootstrap.isCallable(callbackFn)) { 195 throw typeError("not.a.function", ScriptRuntime.safeToString(callbackFn)); 196 } 197 final MethodHandle invoker = Global.instance().getDynamicInvoker(FOREACH_INVOKER_KEY, 198 () -> Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class, Object.class, Object.class, Object.class)); 199 200 final LinkedMap.LinkedMapIterator iterator = set.getJavaMap().getIterator(); 201 for (;;) { 202 final LinkedMap.Node node = iterator.next(); 203 if (node == null) { 204 break; 205 } 206 207 try { 208 final Object result = invoker.invokeExact(callbackFn, thisArg, node.getKey(), node.getKey(), self); 209 } catch (final RuntimeException | Error e) { 210 throw e; 211 } catch (final Throwable t) { 212 throw new RuntimeException(t); 213 } 214 } 215 } 216 217 @Override 218 public String getClassName() { 219 return "Set"; 220 } 221 222 static void populateSet(final LinkedMap map, final Object arg, final Global global) { 223 if (arg != null && arg != Undefined.getUndefined()) { 224 AbstractIterator.iterate(arg, global, value -> map.set(convertKey(value), null)); 225 } 226 } 227 228 LinkedMap getJavaMap() { 229 return map; 230 } 231 232 private static NativeSet getNativeSet(final Object self) { 233 if (self instanceof NativeSet) { 234 return (NativeSet) self; 235 } else { 236 throw typeError("not.a.set", ScriptRuntime.safeToString(self)); 237 } 238 } 239 }