NativeWeakMap.java revision 1626:d99fa86747ee
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
26package jdk.nashorn.internal.objects;
27
28import java.util.Map;
29import java.util.WeakHashMap;
30import jdk.nashorn.internal.objects.annotations.Attribute;
31import jdk.nashorn.internal.objects.annotations.Constructor;
32import jdk.nashorn.internal.objects.annotations.Function;
33import jdk.nashorn.internal.objects.annotations.ScriptClass;
34import jdk.nashorn.internal.runtime.JSType;
35import jdk.nashorn.internal.runtime.PropertyMap;
36import jdk.nashorn.internal.runtime.ScriptObject;
37import jdk.nashorn.internal.runtime.ScriptRuntime;
38import jdk.nashorn.internal.runtime.Undefined;
39
40import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
41import static jdk.nashorn.internal.runtime.JSType.isPrimitive;
42
43/**
44 * This implements the ECMA6 WeakMap object.
45 */
46@ScriptClass("WeakMap")
47public class NativeWeakMap extends ScriptObject {
48
49    private final Map<Object, Object> jmap = new WeakHashMap<>();
50
51    // initialized by nasgen
52    private static PropertyMap $nasgenmap$;
53
54    private NativeWeakMap(final ScriptObject proto, final PropertyMap map) {
55        super(proto, map);
56    }
57
58    /**
59     * ECMA6 23.3.1 The WeakMap Constructor
60     *
61     * @param isNew  whether the new operator used
62     * @param self self reference
63     * @param arg optional iterable argument
64     * @return a new WeakMap object
65     */
66    @Constructor(arity = 0)
67    public static Object construct(final boolean isNew, final Object self, final Object arg) {
68        if (!isNew) {
69            throw typeError("constructor.requires.new", "WeakMap");
70        }
71        final Global global = Global.instance();
72        final NativeWeakMap weakMap = new NativeWeakMap(global.getWeakMapPrototype(), $nasgenmap$);
73        populateMap(weakMap.jmap, arg, global);
74        return weakMap;
75    }
76
77    /**
78     * ECMA6 23.3.3.5 WeakMap.prototype.set ( key , value )
79     *
80     * @param self the self reference
81     * @param key the key
82     * @param value the value
83     * @return this WeakMap object
84     */
85    @Function(attributes = Attribute.NOT_ENUMERABLE)
86    public static Object set(final Object self, final Object key, final Object value) {
87        final NativeWeakMap map = getMap(self);
88        map.jmap.put(checkKey(key), value);
89        return self;
90    }
91
92    /**
93     * ECMA6 23.3.3.3 WeakMap.prototype.get ( key )
94     *
95     * @param self the self reference
96     * @param key the key
97     * @return the associated value or undefined
98     */
99    @Function(attributes = Attribute.NOT_ENUMERABLE)
100    public static Object get(final Object self, final Object key) {
101        final NativeWeakMap map = getMap(self);
102        if (isPrimitive(key)) {
103            return Undefined.getUndefined();
104        }
105        return map.jmap.get(key);
106    }
107
108    /**
109     * ECMA6 23.3.3.2 WeakMap.prototype.delete ( key )
110     *
111     * @param self the self reference
112     * @param key the key to delete
113     * @return true if the key was deleted
114     */
115    @Function(attributes = Attribute.NOT_ENUMERABLE)
116    public static boolean delete(final Object self, final Object key) {
117        final Map<Object, Object> map = getMap(self).jmap;
118        if (isPrimitive(key)) {
119            return false;
120        }
121        final boolean returnValue = map.containsKey(key);
122        map.remove(key);
123        return returnValue;
124    }
125
126    /**
127     * ECMA6 23.3.3.4 WeakMap.prototype.has ( key )
128     *
129     * @param self the self reference
130     * @param key the key
131     * @return true if key is contained
132     */
133    @Function(attributes = Attribute.NOT_ENUMERABLE)
134    public static boolean has(final Object self, final Object key) {
135        final NativeWeakMap map = getMap(self);
136        return !isPrimitive(key) && map.jmap.containsKey(key);
137    }
138
139    @Override
140    public String getClassName() {
141        return "WeakMap";
142    }
143
144    /**
145     * Make sure {@code key} is not a JavaScript primitive value.
146     *
147     * @param key a key object
148     * @return the valid key
149     */
150    static Object checkKey(final Object key) {
151        if (isPrimitive(key)) {
152            throw typeError("invalid.weak.key", ScriptRuntime.safeToString(key));
153        }
154        return key;
155    }
156
157    static void populateMap(final Map<Object, Object> map, final Object arg, final Global global) {
158        // This method is similar to NativeMap.populateMap, but it uses a different
159        // map implementation and the checking/conversion of keys differs as well.
160        if (arg != null && arg != Undefined.getUndefined()) {
161            AbstractIterator.iterate(arg, global, value -> {
162                if (isPrimitive(value)) {
163                    throw typeError(global, "not.an.object", ScriptRuntime.safeToString(value));
164                }
165                if (value instanceof ScriptObject) {
166                    final ScriptObject sobj = (ScriptObject) value;
167                    map.put(checkKey(sobj.get(0)), sobj.get(1));
168                }
169            });
170        }
171    }
172
173    private static NativeWeakMap getMap(final Object self) {
174        if (self instanceof NativeWeakMap) {
175            return (NativeWeakMap)self;
176        } else {
177            throw typeError("not.a.weak.map", ScriptRuntime.safeToString(self));
178        }
179    }
180
181}
182
183
184