~etherpad/etherpad/ubuntu-lucid-backport

« back to all changes in this revision

Viewing changes to infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptableObject.java

  • Committer: James Page
  • Date: 2011-04-13 08:00:43 UTC
  • Revision ID: james.page@canonical.com-20110413080043-eee2nq7y1v7cv2mp
* Refactoring to use native Ubuntu Java libraries. 
* debian/control:
  - use openjdk instead of sun's java
  - update maintainer
* debian/etherpad.init.orig, debian/etherpad.upstart:
  - move the init script out of the way
  - create a basic upstart script
  - note that the open office document conversion daemon was dropped
    from the upstart configuration; if this behavior is desired, please
    create a separate upstart job for it
* debian/rules:
  - just use basic dh_installinit, as it will pick up the new upstart job
* New release
* Changed maintainer to Packaging
* Fixed installation scripts
* Initial Release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * ***** BEGIN LICENSE BLOCK *****
 
4
 * Version: MPL 1.1/GPL 2.0
 
5
 *
 
6
 * The contents of this file are subject to the Mozilla Public License Version
 
7
 * 1.1 (the "License"); you may not use this file except in compliance with
 
8
 * the License. You may obtain a copy of the License at
 
9
 * http://www.mozilla.org/MPL/
 
10
 *
 
11
 * Software distributed under the License is distributed on an "AS IS" basis,
 
12
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
13
 * for the specific language governing rights and limitations under the
 
14
 * License.
 
15
 *
 
16
 * The Original Code is Rhino code, released
 
17
 * May 6, 1999.
 
18
 *
 
19
 * The Initial Developer of the Original Code is
 
20
 * Netscape Communications Corporation.
 
21
 * Portions created by the Initial Developer are Copyright (C) 1997-1999
 
22
 * the Initial Developer. All Rights Reserved.
 
23
 *
 
24
 * Contributor(s):
 
25
 *   Norris Boyd
 
26
 *   Igor Bukanov
 
27
 *   Bob Jervis
 
28
 *   Roger Lawrence
 
29
 *   Steve Weiss
 
30
 *
 
31
 * Alternatively, the contents of this file may be used under the terms of
 
32
 * the GNU General Public License Version 2 or later (the "GPL"), in which
 
33
 * case the provisions of the GPL are applicable instead of those above. If
 
34
 * you wish to allow use of your version of this file only under the terms of
 
35
 * the GPL and not to allow others to use your version of this file under the
 
36
 * MPL, indicate your decision by deleting the provisions above and replacing
 
37
 * them with the notice and other provisions required by the GPL. If you do
 
38
 * not delete the provisions above, a recipient may use your version of this
 
39
 * file under either the MPL or the GPL.
 
40
 *
 
41
 * ***** END LICENSE BLOCK ***** */
 
42
 
 
43
// API class
 
44
 
 
45
package org.mozilla.javascript;
 
46
 
 
47
import java.lang.reflect.*;
 
48
import java.util.Hashtable;
 
49
import java.io.*;
 
50
import org.mozilla.javascript.debug.DebuggableObject;
 
51
 
 
52
/**
 
53
 * This is the default implementation of the Scriptable interface. This
 
54
 * class provides convenient default behavior that makes it easier to
 
55
 * define host objects.
 
56
 * <p>
 
57
 * Various properties and methods of JavaScript objects can be conveniently
 
58
 * defined using methods of ScriptableObject.
 
59
 * <p>
 
60
 * Classes extending ScriptableObject must define the getClassName method.
 
61
 *
 
62
 * @see org.mozilla.javascript.Scriptable
 
63
 * @author Norris Boyd
 
64
 */
 
65
 
 
66
public abstract class ScriptableObject implements Scriptable, Serializable,
 
67
                                                  DebuggableObject,
 
68
                                                  ConstProperties
 
69
{
 
70
 
 
71
    /**
 
72
     * The empty property attribute.
 
73
     *
 
74
     * Used by getAttributes() and setAttributes().
 
75
     *
 
76
     * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
 
77
     * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
 
78
     */
 
79
    public static final int EMPTY =     0x00;
 
80
 
 
81
    /**
 
82
     * Property attribute indicating assignment to this property is ignored.
 
83
     *
 
84
     * @see org.mozilla.javascript.ScriptableObject
 
85
     *      #put(String, Scriptable, Object)
 
86
     * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
 
87
     * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
 
88
     */
 
89
    public static final int READONLY =  0x01;
 
90
 
 
91
    /**
 
92
     * Property attribute indicating property is not enumerated.
 
93
     *
 
94
     * Only enumerated properties will be returned by getIds().
 
95
     *
 
96
     * @see org.mozilla.javascript.ScriptableObject#getIds()
 
97
     * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
 
98
     * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
 
99
     */
 
100
    public static final int DONTENUM =  0x02;
 
101
 
 
102
    /**
 
103
     * Property attribute indicating property cannot be deleted.
 
104
     *
 
105
     * @see org.mozilla.javascript.ScriptableObject#delete(String)
 
106
     * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
 
107
     * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
 
108
     */
 
109
    public static final int PERMANENT = 0x04;
 
110
 
 
111
    /**
 
112
     * Property attribute indicating that this is a const property that has not
 
113
     * been assigned yet.  The first 'const' assignment to the property will
 
114
     * clear this bit.
 
115
     */
 
116
    public static final int UNINITIALIZED_CONST = 0x08;
 
117
 
 
118
    public static final int CONST = PERMANENT|READONLY|UNINITIALIZED_CONST;
 
119
    /**
 
120
     * The prototype of this object.
 
121
     */
 
122
    private Scriptable prototypeObject;
 
123
 
 
124
    /**
 
125
     * The parent scope of this object.
 
126
     */
 
127
    private Scriptable parentScopeObject;
 
128
 
 
129
    private static final Slot REMOVED = new Slot(null, 0, READONLY);
 
130
 
 
131
    static {
 
132
        REMOVED.wasDeleted = 1;
 
133
    }
 
134
 
 
135
    private transient Slot[] slots;
 
136
    // If count >= 0, it gives number of keys or if count < 0,
 
137
    // it indicates sealed object where ~count gives number of keys
 
138
    private int count;
 
139
 
 
140
    // cache; may be removed for smaller memory footprint
 
141
    private transient Slot lastAccess = REMOVED;
 
142
 
 
143
    // associated values are not serialized
 
144
    private transient volatile Hashtable associatedValues;
 
145
 
 
146
    private static final int SLOT_QUERY = 1;
 
147
    private static final int SLOT_MODIFY = 2;
 
148
    private static final int SLOT_REMOVE = 3;
 
149
    private static final int SLOT_MODIFY_GETTER_SETTER = 4;
 
150
    private static final int SLOT_MODIFY_CONST = 5;
 
151
 
 
152
    private static class Slot implements Serializable
 
153
    {
 
154
        static final long serialVersionUID = -3539051633409902634L;
 
155
 
 
156
        String name; // This can change due to caching
 
157
        int indexOrHash;
 
158
        private volatile short attributes;
 
159
        transient volatile byte wasDeleted;
 
160
        volatile Object value;
 
161
        transient volatile Slot next;
 
162
 
 
163
        Slot(String name, int indexOrHash, int attributes)
 
164
        {
 
165
            this.name = name;
 
166
            this.indexOrHash = indexOrHash;
 
167
            this.attributes = (short)attributes;
 
168
        }
 
169
 
 
170
        private void readObject(ObjectInputStream in)
 
171
            throws IOException, ClassNotFoundException
 
172
        {
 
173
            in.defaultReadObject();
 
174
            if (name != null) {
 
175
                indexOrHash = name.hashCode();
 
176
            }
 
177
        }
 
178
 
 
179
        final int getAttributes()
 
180
        {
 
181
            return attributes;
 
182
        }
 
183
 
 
184
        final synchronized void setAttributes(int value)
 
185
        {
 
186
            checkValidAttributes(value);
 
187
            attributes = (short)value;
 
188
        }
 
189
 
 
190
        final void checkNotReadonly()
 
191
        {
 
192
            if ((attributes & READONLY) != 0) {
 
193
                String str = (name != null ? name
 
194
                              : Integer.toString(indexOrHash));
 
195
                throw Context.reportRuntimeError1("msg.modify.readonly", str);
 
196
            }
 
197
        }
 
198
 
 
199
    }
 
200
 
 
201
    private static final class GetterSlot extends Slot
 
202
    {
 
203
        static final long serialVersionUID = -4900574849788797588L;
 
204
 
 
205
        Object getter;
 
206
        Object setter;
 
207
 
 
208
        GetterSlot(String name, int indexOrHash, int attributes)
 
209
        {
 
210
            super(name, indexOrHash, attributes);
 
211
        }
 
212
    }
 
213
 
 
214
    static void checkValidAttributes(int attributes)
 
215
    {
 
216
        final int mask = READONLY | DONTENUM | PERMANENT | UNINITIALIZED_CONST;
 
217
        if ((attributes & ~mask) != 0) {
 
218
            throw new IllegalArgumentException(String.valueOf(attributes));
 
219
        }
 
220
    }
 
221
 
 
222
    public ScriptableObject()
 
223
    {
 
224
    }
 
225
 
 
226
    public ScriptableObject(Scriptable scope, Scriptable prototype)
 
227
    {
 
228
        if (scope == null)
 
229
            throw new IllegalArgumentException();
 
230
 
 
231
        parentScopeObject = scope;
 
232
        prototypeObject = prototype;
 
233
    }
 
234
 
 
235
    /**
 
236
     * Return the name of the class.
 
237
     *
 
238
     * This is typically the same name as the constructor.
 
239
     * Classes extending ScriptableObject must implement this abstract
 
240
     * method.
 
241
     */
 
242
    public abstract String getClassName();
 
243
 
 
244
    /**
 
245
     * Returns true if the named property is defined.
 
246
     *
 
247
     * @param name the name of the property
 
248
     * @param start the object in which the lookup began
 
249
     * @return true if and only if the property was found in the object
 
250
     */
 
251
    public boolean has(String name, Scriptable start)
 
252
    {
 
253
        return null != getSlot(name, 0, SLOT_QUERY);
 
254
    }
 
255
 
 
256
    /**
 
257
     * Returns true if the property index is defined.
 
258
     *
 
259
     * @param index the numeric index for the property
 
260
     * @param start the object in which the lookup began
 
261
     * @return true if and only if the property was found in the object
 
262
     */
 
263
    public boolean has(int index, Scriptable start)
 
264
    {
 
265
        return null != getSlot(null, index, SLOT_QUERY);
 
266
    }
 
267
 
 
268
    /**
 
269
     * Returns the value of the named property or NOT_FOUND.
 
270
     *
 
271
     * If the property was created using defineProperty, the
 
272
     * appropriate getter method is called.
 
273
     *
 
274
     * @param name the name of the property
 
275
     * @param start the object in which the lookup began
 
276
     * @return the value of the property (may be null), or NOT_FOUND
 
277
     */
 
278
    public Object get(String name, Scriptable start)
 
279
    {
 
280
        return getImpl(name, 0, start);
 
281
    }
 
282
 
 
283
    /**
 
284
     * Returns the value of the indexed property or NOT_FOUND.
 
285
     *
 
286
     * @param index the numeric index for the property
 
287
     * @param start the object in which the lookup began
 
288
     * @return the value of the property (may be null), or NOT_FOUND
 
289
     */
 
290
    public Object get(int index, Scriptable start)
 
291
    {
 
292
        return getImpl(null, index, start);
 
293
    }
 
294
 
 
295
    /**
 
296
     * Sets the value of the named property, creating it if need be.
 
297
     *
 
298
     * If the property was created using defineProperty, the
 
299
     * appropriate setter method is called. <p>
 
300
     *
 
301
     * If the property's attributes include READONLY, no action is
 
302
     * taken.
 
303
     * This method will actually set the property in the start
 
304
     * object.
 
305
     *
 
306
     * @param name the name of the property
 
307
     * @param start the object whose property is being set
 
308
     * @param value value to set the property to
 
309
     */
 
310
    public void put(String name, Scriptable start, Object value)
 
311
    {
 
312
        if (putImpl(name, 0, start, value, EMPTY))
 
313
            return;
 
314
 
 
315
        if (start == this) throw Kit.codeBug();
 
316
        start.put(name, start, value);
 
317
    }
 
318
 
 
319
    /**
 
320
     * Sets the value of the indexed property, creating it if need be.
 
321
     *
 
322
     * @param index the numeric index for the property
 
323
     * @param start the object whose property is being set
 
324
     * @param value value to set the property to
 
325
     */
 
326
    public void put(int index, Scriptable start, Object value)
 
327
    {
 
328
        if (putImpl(null, index, start, value, EMPTY))
 
329
            return;
 
330
 
 
331
        if (start == this) throw Kit.codeBug();
 
332
        start.put(index, start, value);
 
333
    }
 
334
 
 
335
    /**
 
336
     * Removes a named property from the object.
 
337
     *
 
338
     * If the property is not found, or it has the PERMANENT attribute,
 
339
     * no action is taken.
 
340
     *
 
341
     * @param name the name of the property
 
342
     */
 
343
    public void delete(String name)
 
344
    {
 
345
        checkNotSealed(name, 0);
 
346
        accessSlot(name, 0, SLOT_REMOVE);
 
347
    }
 
348
 
 
349
    /**
 
350
     * Removes the indexed property from the object.
 
351
     *
 
352
     * If the property is not found, or it has the PERMANENT attribute,
 
353
     * no action is taken.
 
354
     *
 
355
     * @param index the numeric index for the property
 
356
     */
 
357
    public void delete(int index)
 
358
    {
 
359
        checkNotSealed(null, index);
 
360
        accessSlot(null, index, SLOT_REMOVE);
 
361
    }
 
362
 
 
363
    /**
 
364
     * Sets the value of the named const property, creating it if need be.
 
365
     *
 
366
     * If the property was created using defineProperty, the
 
367
     * appropriate setter method is called. <p>
 
368
     *
 
369
     * If the property's attributes include READONLY, no action is
 
370
     * taken.
 
371
     * This method will actually set the property in the start
 
372
     * object.
 
373
     *
 
374
     * @param name the name of the property
 
375
     * @param start the object whose property is being set
 
376
     * @param value value to set the property to
 
377
     */
 
378
    public void putConst(String name, Scriptable start, Object value)
 
379
    {
 
380
        if (putImpl(name, 0, start, value, READONLY))
 
381
            return;
 
382
 
 
383
        if (start == this) throw Kit.codeBug();
 
384
        if (start instanceof ConstProperties)
 
385
            ((ConstProperties)start).putConst(name, start, value);
 
386
        else
 
387
            start.put(name, start, value);
 
388
    }
 
389
 
 
390
    public void defineConst(String name, Scriptable start)
 
391
    {
 
392
        if (putImpl(name, 0, start, Undefined.instance, UNINITIALIZED_CONST))
 
393
            return;
 
394
 
 
395
        if (start == this) throw Kit.codeBug();
 
396
        if (start instanceof ConstProperties)
 
397
            ((ConstProperties)start).defineConst(name, start);
 
398
    }
 
399
    /**
 
400
     * Returns true if the named property is defined as a const on this object.
 
401
     * @param name
 
402
     * @return true if the named property is defined as a const, false
 
403
     * otherwise.
 
404
     */
 
405
    public boolean isConst(String name)
 
406
    {
 
407
        Slot slot = getSlot(name, 0, SLOT_QUERY);
 
408
        if (slot == null) {
 
409
            return false;
 
410
        }
 
411
        return (slot.getAttributes() & (PERMANENT|READONLY)) ==
 
412
                                       (PERMANENT|READONLY);
 
413
 
 
414
    }
 
415
    /**
 
416
     * @deprecated Use {@link #getAttributes(String name)}. The engine always
 
417
     * ignored the start argument.
 
418
     */
 
419
    public final int getAttributes(String name, Scriptable start)
 
420
    {
 
421
        return getAttributes(name);
 
422
    }
 
423
 
 
424
    /**
 
425
     * @deprecated Use {@link #getAttributes(int index)}. The engine always
 
426
     * ignored the start argument.
 
427
     */
 
428
    public final int getAttributes(int index, Scriptable start)
 
429
    {
 
430
        return getAttributes(index);
 
431
    }
 
432
 
 
433
    /**
 
434
     * @deprecated Use {@link #setAttributes(String name, int attributes)}.
 
435
     * The engine always ignored the start argument.
 
436
     */
 
437
    public final void setAttributes(String name, Scriptable start,
 
438
                                    int attributes)
 
439
    {
 
440
        setAttributes(name, attributes);
 
441
    }
 
442
 
 
443
    /**
 
444
     * @deprecated Use {@link #setAttributes(int index, int attributes)}.
 
445
     * The engine always ignored the start argument.
 
446
     */
 
447
    public void setAttributes(int index, Scriptable start,
 
448
                              int attributes)
 
449
    {
 
450
        setAttributes(index, attributes);
 
451
    }
 
452
 
 
453
    /**
 
454
     * Get the attributes of a named property.
 
455
     *
 
456
     * The property is specified by <code>name</code>
 
457
     * as defined for <code>has</code>.<p>
 
458
     *
 
459
     * @param name the identifier for the property
 
460
     * @return the bitset of attributes
 
461
     * @exception EvaluatorException if the named property is not found
 
462
     * @see org.mozilla.javascript.ScriptableObject#has(String, Scriptable)
 
463
     * @see org.mozilla.javascript.ScriptableObject#READONLY
 
464
     * @see org.mozilla.javascript.ScriptableObject#DONTENUM
 
465
     * @see org.mozilla.javascript.ScriptableObject#PERMANENT
 
466
     * @see org.mozilla.javascript.ScriptableObject#EMPTY
 
467
     */
 
468
    public int getAttributes(String name)
 
469
    {
 
470
        return findAttributeSlot(name, 0, SLOT_QUERY).getAttributes();
 
471
    }
 
472
 
 
473
    /**
 
474
     * Get the attributes of an indexed property.
 
475
     *
 
476
     * @param index the numeric index for the property
 
477
     * @exception EvaluatorException if the named property is not found
 
478
     *            is not found
 
479
     * @return the bitset of attributes
 
480
     * @see org.mozilla.javascript.ScriptableObject#has(String, Scriptable)
 
481
     * @see org.mozilla.javascript.ScriptableObject#READONLY
 
482
     * @see org.mozilla.javascript.ScriptableObject#DONTENUM
 
483
     * @see org.mozilla.javascript.ScriptableObject#PERMANENT
 
484
     * @see org.mozilla.javascript.ScriptableObject#EMPTY
 
485
     */
 
486
    public int getAttributes(int index)
 
487
    {
 
488
        return findAttributeSlot(null, index, SLOT_QUERY).getAttributes();
 
489
    }
 
490
 
 
491
    /**
 
492
     * Set the attributes of a named property.
 
493
     *
 
494
     * The property is specified by <code>name</code>
 
495
     * as defined for <code>has</code>.<p>
 
496
     *
 
497
     * The possible attributes are READONLY, DONTENUM,
 
498
     * and PERMANENT. Combinations of attributes
 
499
     * are expressed by the bitwise OR of attributes.
 
500
     * EMPTY is the state of no attributes set. Any unused
 
501
     * bits are reserved for future use.
 
502
     *
 
503
     * @param name the name of the property
 
504
     * @param attributes the bitset of attributes
 
505
     * @exception EvaluatorException if the named property is not found
 
506
     * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
 
507
     * @see org.mozilla.javascript.ScriptableObject#READONLY
 
508
     * @see org.mozilla.javascript.ScriptableObject#DONTENUM
 
509
     * @see org.mozilla.javascript.ScriptableObject#PERMANENT
 
510
     * @see org.mozilla.javascript.ScriptableObject#EMPTY
 
511
     */
 
512
    public void setAttributes(String name, int attributes)
 
513
    {
 
514
        checkNotSealed(name, 0);
 
515
        findAttributeSlot(name, 0, SLOT_MODIFY).setAttributes(attributes);
 
516
    }
 
517
 
 
518
    /**
 
519
     * Set the attributes of an indexed property.
 
520
     *
 
521
     * @param index the numeric index for the property
 
522
     * @param attributes the bitset of attributes
 
523
     * @exception EvaluatorException if the named property is not found
 
524
     * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
 
525
     * @see org.mozilla.javascript.ScriptableObject#READONLY
 
526
     * @see org.mozilla.javascript.ScriptableObject#DONTENUM
 
527
     * @see org.mozilla.javascript.ScriptableObject#PERMANENT
 
528
     * @see org.mozilla.javascript.ScriptableObject#EMPTY
 
529
     */
 
530
    public void setAttributes(int index, int attributes)
 
531
    {
 
532
        checkNotSealed(null, index);
 
533
        findAttributeSlot(null, index, SLOT_MODIFY).setAttributes(attributes);
 
534
    }
 
535
 
 
536
    /**
 
537
     * XXX: write docs.
 
538
     */
 
539
    public void setGetterOrSetter(String name, int index,
 
540
                                  Callable getterOrSeter, boolean isSetter)
 
541
    {
 
542
        if (name != null && index != 0)
 
543
            throw new IllegalArgumentException(name);
 
544
 
 
545
        checkNotSealed(name, index);
 
546
        GetterSlot gslot = (GetterSlot)getSlot(name, index,
 
547
                                               SLOT_MODIFY_GETTER_SETTER);
 
548
        gslot.checkNotReadonly();
 
549
        if (isSetter) {
 
550
            gslot.setter = getterOrSeter;
 
551
        } else {
 
552
            gslot.getter = getterOrSeter;
 
553
        }
 
554
        gslot.value = Undefined.instance;
 
555
    }
 
556
    
 
557
    /**
 
558
     * Get the getter or setter for a given property. Used by __lookupGetter__
 
559
     * and __lookupSetter__.
 
560
     * 
 
561
     * @param name Name of the object. If nonnull, index must be 0.
 
562
     * @param index Index of the object. If nonzero, name must be null.
 
563
     * @param isSetter If true, return the setter, otherwise return the getter.
 
564
     * @exception IllegalArgumentException if both name and index are nonnull
 
565
     *            and nonzero respectively.
 
566
     * @return Null if the property does not exist. Otherwise returns either 
 
567
     *         the getter or the setter for the property, depending on 
 
568
     *         the value of isSetter (may be undefined if unset).
 
569
     */
 
570
    public Object getGetterOrSetter(String name, int index, boolean isSetter)
 
571
    {
 
572
        if (name != null && index != 0)
 
573
            throw new IllegalArgumentException(name);
 
574
        Slot slot = getSlot(name, index, SLOT_QUERY);
 
575
        if (slot == null)
 
576
            return null;
 
577
        if (slot instanceof GetterSlot) {
 
578
            GetterSlot gslot = (GetterSlot)slot;
 
579
            Object result = isSetter ? gslot.setter : gslot.getter;
 
580
            return result != null ? result : Undefined.instance;
 
581
        } else
 
582
            return Undefined.instance;
 
583
    }
 
584
 
 
585
    /**
 
586
     * Returns whether a property is a getter or a setter
 
587
     * @param name property name
 
588
     * @param index property index
 
589
     * @param setter true to check for a setter, false for a getter
 
590
     * @return whether the property is a getter or a setter
 
591
     */
 
592
    protected boolean isGetterOrSetter(String name, int index, boolean setter) {
 
593
        Slot slot = getSlot(name, index, SLOT_QUERY);
 
594
        if (slot instanceof GetterSlot) {
 
595
            if (setter && ((GetterSlot)slot).setter != null) return true;
 
596
            if (!setter && ((GetterSlot)slot).getter != null) return true;
 
597
        }
 
598
        return false;
 
599
    }
 
600
 
 
601
    void addLazilyInitializedValue(String name, int index,
 
602
                                   LazilyLoadedCtor init, int attributes)
 
603
    {
 
604
        if (name != null && index != 0)
 
605
            throw new IllegalArgumentException(name);
 
606
        checkNotSealed(name, index);
 
607
        GetterSlot gslot = (GetterSlot)getSlot(name, index,
 
608
                                               SLOT_MODIFY_GETTER_SETTER);
 
609
        gslot.setAttributes(attributes);
 
610
        gslot.getter = null;
 
611
        gslot.setter = null;
 
612
        gslot.value = init;
 
613
    }
 
614
 
 
615
    /**
 
616
     * Returns the prototype of the object.
 
617
     */
 
618
    public Scriptable getPrototype()
 
619
    {
 
620
        return prototypeObject;
 
621
    }
 
622
 
 
623
    /**
 
624
     * Sets the prototype of the object.
 
625
     */
 
626
    public void setPrototype(Scriptable m)
 
627
    {
 
628
        prototypeObject = m;
 
629
    }
 
630
 
 
631
    /**
 
632
     * Returns the parent (enclosing) scope of the object.
 
633
     */
 
634
    public Scriptable getParentScope()
 
635
    {
 
636
        return parentScopeObject;
 
637
    }
 
638
 
 
639
    /**
 
640
     * Sets the parent (enclosing) scope of the object.
 
641
     */
 
642
    public void setParentScope(Scriptable m)
 
643
    {
 
644
        parentScopeObject = m;
 
645
    }
 
646
 
 
647
    /**
 
648
     * Returns an array of ids for the properties of the object.
 
649
     *
 
650
     * <p>Any properties with the attribute DONTENUM are not listed. <p>
 
651
     *
 
652
     * @return an array of java.lang.Objects with an entry for every
 
653
     * listed property. Properties accessed via an integer index will
 
654
     * have a corresponding
 
655
     * Integer entry in the returned array. Properties accessed by
 
656
     * a String will have a String entry in the returned array.
 
657
     */
 
658
    public Object[] getIds() {
 
659
        return getIds(false);
 
660
    }
 
661
 
 
662
    /**
 
663
     * Returns an array of ids for the properties of the object.
 
664
     *
 
665
     * <p>All properties, even those with attribute DONTENUM, are listed. <p>
 
666
     *
 
667
     * @return an array of java.lang.Objects with an entry for every
 
668
     * listed property. Properties accessed via an integer index will
 
669
     * have a corresponding
 
670
     * Integer entry in the returned array. Properties accessed by
 
671
     * a String will have a String entry in the returned array.
 
672
     */
 
673
    public Object[] getAllIds() {
 
674
        return getIds(true);
 
675
    }
 
676
 
 
677
    /**
 
678
     * Implements the [[DefaultValue]] internal method.
 
679
     *
 
680
     * <p>Note that the toPrimitive conversion is a no-op for
 
681
     * every type other than Object, for which [[DefaultValue]]
 
682
     * is called. See ECMA 9.1.<p>
 
683
     *
 
684
     * A <code>hint</code> of null means "no hint".
 
685
     *
 
686
     * @param typeHint the type hint
 
687
     * @return the default value for the object
 
688
     *
 
689
     * See ECMA 8.6.2.6.
 
690
     */
 
691
    public Object getDefaultValue(Class typeHint)
 
692
    {
 
693
        return getDefaultValue(this, typeHint);
 
694
    }
 
695
    
 
696
    public static Object getDefaultValue(Scriptable object, Class typeHint)
 
697
    {
 
698
        Context cx = null;
 
699
        for (int i=0; i < 2; i++) {
 
700
            boolean tryToString;
 
701
            if (typeHint == ScriptRuntime.StringClass) {
 
702
                tryToString = (i == 0);
 
703
            } else {
 
704
                tryToString = (i == 1);
 
705
            }
 
706
 
 
707
            String methodName;
 
708
            Object[] args;
 
709
            if (tryToString) {
 
710
                methodName = "toString";
 
711
                args = ScriptRuntime.emptyArgs;
 
712
            } else {
 
713
                methodName = "valueOf";
 
714
                args = new Object[1];
 
715
                String hint;
 
716
                if (typeHint == null) {
 
717
                    hint = "undefined";
 
718
                } else if (typeHint == ScriptRuntime.StringClass) {
 
719
                    hint = "string";
 
720
                } else if (typeHint == ScriptRuntime.ScriptableClass) {
 
721
                    hint = "object";
 
722
                } else if (typeHint == ScriptRuntime.FunctionClass) {
 
723
                    hint = "function";
 
724
                } else if (typeHint == ScriptRuntime.BooleanClass
 
725
                           || typeHint == Boolean.TYPE)
 
726
                {
 
727
                    hint = "boolean";
 
728
                } else if (typeHint == ScriptRuntime.NumberClass ||
 
729
                         typeHint == ScriptRuntime.ByteClass ||
 
730
                         typeHint == Byte.TYPE ||
 
731
                         typeHint == ScriptRuntime.ShortClass ||
 
732
                         typeHint == Short.TYPE ||
 
733
                         typeHint == ScriptRuntime.IntegerClass ||
 
734
                         typeHint == Integer.TYPE ||
 
735
                         typeHint == ScriptRuntime.FloatClass ||
 
736
                         typeHint == Float.TYPE ||
 
737
                         typeHint == ScriptRuntime.DoubleClass ||
 
738
                         typeHint == Double.TYPE)
 
739
                {
 
740
                    hint = "number";
 
741
                } else {
 
742
                    throw Context.reportRuntimeError1(
 
743
                        "msg.invalid.type", typeHint.toString());
 
744
                }
 
745
                args[0] = hint;
 
746
            }
 
747
            Object v = getProperty(object, methodName);
 
748
            if (!(v instanceof Function))
 
749
                continue;
 
750
            Function fun = (Function) v;
 
751
            if (cx == null)
 
752
                cx = Context.getContext();
 
753
            v = fun.call(cx, fun.getParentScope(), object, args);
 
754
            if (v != null) {
 
755
                if (!(v instanceof Scriptable)) {
 
756
                    return v;
 
757
                }
 
758
                if (typeHint == ScriptRuntime.ScriptableClass
 
759
                    || typeHint == ScriptRuntime.FunctionClass)
 
760
                {
 
761
                    return v;
 
762
                }
 
763
                if (tryToString && v instanceof Wrapper) {
 
764
                    // Let a wrapped java.lang.String pass for a primitive
 
765
                    // string.
 
766
                    Object u = ((Wrapper)v).unwrap();
 
767
                    if (u instanceof String)
 
768
                        return u;
 
769
                }
 
770
            }
 
771
        }
 
772
        // fall through to error
 
773
        String arg = (typeHint == null) ? "undefined" : typeHint.getName();
 
774
        throw ScriptRuntime.typeError1("msg.default.value", arg);
 
775
    }
 
776
 
 
777
    /**
 
778
     * Implements the instanceof operator.
 
779
     *
 
780
     * <p>This operator has been proposed to ECMA.
 
781
     *
 
782
     * @param instance The value that appeared on the LHS of the instanceof
 
783
     *              operator
 
784
     * @return true if "this" appears in value's prototype chain
 
785
     *
 
786
     */
 
787
    public boolean hasInstance(Scriptable instance) {
 
788
        // Default for JS objects (other than Function) is to do prototype
 
789
        // chasing.  This will be overridden in NativeFunction and non-JS
 
790
        // objects.
 
791
 
 
792
        return ScriptRuntime.jsDelegatesTo(instance, this);
 
793
    }
 
794
    
 
795
    /**
 
796
     * Emulate the SpiderMonkey (and Firefox) feature of allowing
 
797
     * custom objects to avoid detection by normal "object detection"
 
798
     * code patterns. This is used to implement document.all.
 
799
     * See https://bugzilla.mozilla.org/show_bug.cgi?id=412247.
 
800
     * This is an analog to JOF_DETECTING from SpiderMonkey; see
 
801
     * https://bugzilla.mozilla.org/show_bug.cgi?id=248549.
 
802
     * Other than this special case, embeddings should return false.
 
803
     * @return true if this object should avoid object detection
 
804
     * @since 1.7R1
 
805
     */
 
806
    public boolean avoidObjectDetection() {
 
807
        return false;
 
808
    }
 
809
 
 
810
    /**
 
811
     * Custom <tt>==</tt> operator.
 
812
     * Must return {@link Scriptable#NOT_FOUND} if this object does not
 
813
     * have custom equality operator for the given value,
 
814
     * <tt>Boolean.TRUE</tt> if this object is equivalent to <tt>value</tt>,
 
815
     * <tt>Boolean.FALSE</tt> if this object is not equivalent to
 
816
     * <tt>value</tt>.
 
817
     * <p>
 
818
     * The default implementation returns Boolean.TRUE
 
819
     * if <tt>this == value</tt> or {@link Scriptable#NOT_FOUND} otherwise.
 
820
     * It indicates that by default custom equality is available only if
 
821
     * <tt>value</tt> is <tt>this</tt> in which case true is returned.
 
822
     */
 
823
    protected Object equivalentValues(Object value)
 
824
    {
 
825
        return (this == value) ? Boolean.TRUE : Scriptable.NOT_FOUND;
 
826
    }
 
827
 
 
828
    /**
 
829
     * Defines JavaScript objects from a Java class that implements Scriptable.
 
830
     *
 
831
     * If the given class has a method
 
832
     * <pre>
 
833
     * static void init(Context cx, Scriptable scope, boolean sealed);</pre>
 
834
     *
 
835
     * or its compatibility form
 
836
     * <pre>
 
837
     * static void init(Scriptable scope);</pre>
 
838
     *
 
839
     * then it is invoked and no further initialization is done.<p>
 
840
     *
 
841
     * However, if no such a method is found, then the class's constructors and
 
842
     * methods are used to initialize a class in the following manner.<p>
 
843
     *
 
844
     * First, the zero-parameter constructor of the class is called to
 
845
     * create the prototype. If no such constructor exists,
 
846
     * a {@link EvaluatorException} is thrown. <p>
 
847
     *
 
848
     * Next, all methods are scanned for special prefixes that indicate that they
 
849
     * have special meaning for defining JavaScript objects.
 
850
     * These special prefixes are
 
851
     * <ul>
 
852
     * <li><code>jsFunction_</code> for a JavaScript function
 
853
     * <li><code>jsStaticFunction_</code> for a JavaScript function that
 
854
     *           is a property of the constructor
 
855
     * <li><code>jsGet_</code> for a getter of a JavaScript property
 
856
     * <li><code>jsSet_</code> for a setter of a JavaScript property
 
857
     * <li><code>jsConstructor</code> for a JavaScript function that
 
858
     *           is the constructor
 
859
     * </ul><p>
 
860
     *
 
861
     * If the method's name begins with "jsFunction_", a JavaScript function
 
862
     * is created with a name formed from the rest of the Java method name
 
863
     * following "jsFunction_". So a Java method named "jsFunction_foo" will
 
864
     * define a JavaScript method "foo". Calling this JavaScript function
 
865
     * will cause the Java method to be called. The parameters of the method
 
866
     * must be of number and types as defined by the FunctionObject class.
 
867
     * The JavaScript function is then added as a property
 
868
     * of the prototype. <p>
 
869
     *
 
870
     * If the method's name begins with "jsStaticFunction_", it is handled
 
871
     * similarly except that the resulting JavaScript function is added as a
 
872
     * property of the constructor object. The Java method must be static.
 
873
     *
 
874
     * If the method's name begins with "jsGet_" or "jsSet_", the method is
 
875
     * considered to define a property. Accesses to the defined property
 
876
     * will result in calls to these getter and setter methods. If no
 
877
     * setter is defined, the property is defined as READONLY.<p>
 
878
     *
 
879
     * If the method's name is "jsConstructor", the method is
 
880
     * considered to define the body of the constructor. Only one
 
881
     * method of this name may be defined.
 
882
     * If no method is found that can serve as constructor, a Java
 
883
     * constructor will be selected to serve as the JavaScript
 
884
     * constructor in the following manner. If the class has only one
 
885
     * Java constructor, that constructor is used to define
 
886
     * the JavaScript constructor. If the the class has two constructors,
 
887
     * one must be the zero-argument constructor (otherwise an
 
888
     * {@link EvaluatorException} would have already been thrown
 
889
     * when the prototype was to be created). In this case
 
890
     * the Java constructor with one or more parameters will be used
 
891
     * to define the JavaScript constructor. If the class has three
 
892
     * or more constructors, an {@link EvaluatorException}
 
893
     * will be thrown.<p>
 
894
     *
 
895
     * Finally, if there is a method
 
896
     * <pre>
 
897
     * static void finishInit(Scriptable scope, FunctionObject constructor,
 
898
     *                        Scriptable prototype)</pre>
 
899
     *
 
900
     * it will be called to finish any initialization. The <code>scope</code>
 
901
     * argument will be passed, along with the newly created constructor and
 
902
     * the newly created prototype.<p>
 
903
     *
 
904
     * @param scope The scope in which to define the constructor.
 
905
     * @param clazz The Java class to use to define the JavaScript objects
 
906
     *              and properties.
 
907
     * @exception IllegalAccessException if access is not available
 
908
     *            to a reflected class member
 
909
     * @exception InstantiationException if unable to instantiate
 
910
     *            the named class
 
911
     * @exception InvocationTargetException if an exception is thrown
 
912
     *            during execution of methods of the named class
 
913
     * @see org.mozilla.javascript.Function
 
914
     * @see org.mozilla.javascript.FunctionObject
 
915
     * @see org.mozilla.javascript.ScriptableObject#READONLY
 
916
     * @see org.mozilla.javascript.ScriptableObject
 
917
     *      #defineProperty(String, Class, int)
 
918
     */
 
919
    public static void defineClass(Scriptable scope, Class clazz)
 
920
        throws IllegalAccessException, InstantiationException,
 
921
               InvocationTargetException
 
922
    {
 
923
        defineClass(scope, clazz, false, false);
 
924
    }
 
925
 
 
926
    /**
 
927
     * Defines JavaScript objects from a Java class, optionally
 
928
     * allowing sealing.
 
929
     *
 
930
     * Similar to <code>defineClass(Scriptable scope, Class clazz)</code>
 
931
     * except that sealing is allowed. An object that is sealed cannot have
 
932
     * properties added or removed. Note that sealing is not allowed in
 
933
     * the current ECMA/ISO language specification, but is likely for
 
934
     * the next version.
 
935
     *
 
936
     * @param scope The scope in which to define the constructor.
 
937
     * @param clazz The Java class to use to define the JavaScript objects
 
938
     *              and properties. The class must implement Scriptable.
 
939
     * @param sealed Whether or not to create sealed standard objects that
 
940
     *               cannot be modified.
 
941
     * @exception IllegalAccessException if access is not available
 
942
     *            to a reflected class member
 
943
     * @exception InstantiationException if unable to instantiate
 
944
     *            the named class
 
945
     * @exception InvocationTargetException if an exception is thrown
 
946
     *            during execution of methods of the named class
 
947
     * @since 1.4R3
 
948
     */
 
949
    public static void defineClass(Scriptable scope, Class clazz,
 
950
                                   boolean sealed)
 
951
        throws IllegalAccessException, InstantiationException,
 
952
               InvocationTargetException
 
953
    {
 
954
        defineClass(scope, clazz, sealed, false);
 
955
    }
 
956
 
 
957
    /**
 
958
     * Defines JavaScript objects from a Java class, optionally
 
959
     * allowing sealing and mapping of Java inheritance to JavaScript
 
960
     * prototype-based inheritance.
 
961
     *
 
962
     * Similar to <code>defineClass(Scriptable scope, Class clazz)</code>
 
963
     * except that sealing and inheritance mapping are allowed. An object
 
964
     * that is sealed cannot have properties added or removed. Note that
 
965
     * sealing is not allowed in the current ECMA/ISO language specification,
 
966
     * but is likely for the next version.
 
967
     *
 
968
     * @param scope The scope in which to define the constructor.
 
969
     * @param clazz The Java class to use to define the JavaScript objects
 
970
     *              and properties. The class must implement Scriptable.
 
971
     * @param sealed Whether or not to create sealed standard objects that
 
972
     *               cannot be modified.
 
973
     * @param mapInheritance Whether or not to map Java inheritance to
 
974
     *                       JavaScript prototype-based inheritance.
 
975
     * @return the class name for the prototype of the specified class
 
976
     * @exception IllegalAccessException if access is not available
 
977
     *            to a reflected class member
 
978
     * @exception InstantiationException if unable to instantiate
 
979
     *            the named class
 
980
     * @exception InvocationTargetException if an exception is thrown
 
981
     *            during execution of methods of the named class
 
982
     * @since 1.6R2
 
983
     */
 
984
    public static String defineClass(Scriptable scope, Class clazz,
 
985
                                     boolean sealed, boolean mapInheritance)
 
986
        throws IllegalAccessException, InstantiationException,
 
987
               InvocationTargetException
 
988
    {
 
989
        BaseFunction ctor = buildClassCtor(scope, clazz, sealed,
 
990
                                           mapInheritance);
 
991
        if (ctor == null)
 
992
            return null;
 
993
        String name = ctor.getClassPrototype().getClassName();
 
994
        defineProperty(scope, name, ctor, ScriptableObject.DONTENUM);
 
995
        return name;
 
996
    }
 
997
 
 
998
    static BaseFunction buildClassCtor(Scriptable scope, Class clazz,
 
999
                                       boolean sealed,
 
1000
                                       boolean mapInheritance)
 
1001
        throws IllegalAccessException, InstantiationException,
 
1002
               InvocationTargetException
 
1003
    {
 
1004
        Method[] methods = FunctionObject.getMethodList(clazz);
 
1005
        for (int i=0; i < methods.length; i++) {
 
1006
            Method method = methods[i];
 
1007
            if (!method.getName().equals("init"))
 
1008
                continue;
 
1009
            Class[] parmTypes = method.getParameterTypes();
 
1010
            if (parmTypes.length == 3 &&
 
1011
                parmTypes[0] == ScriptRuntime.ContextClass &&
 
1012
                parmTypes[1] == ScriptRuntime.ScriptableClass &&
 
1013
                parmTypes[2] == Boolean.TYPE &&
 
1014
                Modifier.isStatic(method.getModifiers()))
 
1015
            {
 
1016
                Object args[] = { Context.getContext(), scope,
 
1017
                                  sealed ? Boolean.TRUE : Boolean.FALSE };
 
1018
                method.invoke(null, args);
 
1019
                return null;
 
1020
            }
 
1021
            if (parmTypes.length == 1 &&
 
1022
                parmTypes[0] == ScriptRuntime.ScriptableClass &&
 
1023
                Modifier.isStatic(method.getModifiers()))
 
1024
            {
 
1025
                Object args[] = { scope };
 
1026
                method.invoke(null, args);
 
1027
                return null;
 
1028
            }
 
1029
 
 
1030
        }
 
1031
 
 
1032
        // If we got here, there isn't an "init" method with the right
 
1033
        // parameter types.
 
1034
 
 
1035
        Constructor[] ctors = clazz.getConstructors();
 
1036
        Constructor protoCtor = null;
 
1037
        for (int i=0; i < ctors.length; i++) {
 
1038
            if (ctors[i].getParameterTypes().length == 0) {
 
1039
                protoCtor = ctors[i];
 
1040
                break;
 
1041
            }
 
1042
        }
 
1043
        if (protoCtor == null) {
 
1044
            throw Context.reportRuntimeError1(
 
1045
                      "msg.zero.arg.ctor", clazz.getName());
 
1046
        }
 
1047
 
 
1048
        Scriptable proto = (Scriptable) protoCtor.newInstance(ScriptRuntime.emptyArgs);
 
1049
        String className = proto.getClassName();
 
1050
 
 
1051
        // Set the prototype's prototype, trying to map Java inheritance to JS
 
1052
        // prototype-based inheritance if requested to do so.
 
1053
        Scriptable superProto = null;
 
1054
        if (mapInheritance) {
 
1055
            Class superClass = clazz.getSuperclass();
 
1056
            if (ScriptRuntime.ScriptableClass.isAssignableFrom(superClass)
 
1057
                    && !Modifier.isAbstract(superClass.getModifiers())) {
 
1058
                String name = ScriptableObject.defineClass(scope, superClass, sealed, mapInheritance);
 
1059
                if (name != null) {
 
1060
                    superProto = ScriptableObject.getClassPrototype(scope, name);
 
1061
                }
 
1062
            }
 
1063
        }
 
1064
        if (superProto == null) {
 
1065
            superProto = ScriptableObject.getObjectPrototype(scope);
 
1066
        }
 
1067
        proto.setPrototype(superProto);
 
1068
 
 
1069
        // Find out whether there are any methods that begin with
 
1070
        // "js". If so, then only methods that begin with special
 
1071
        // prefixes will be defined as JavaScript entities.
 
1072
        final String functionPrefix = "jsFunction_";
 
1073
        final String staticFunctionPrefix = "jsStaticFunction_";
 
1074
        final String getterPrefix = "jsGet_";
 
1075
        final String setterPrefix = "jsSet_";
 
1076
        final String ctorName = "jsConstructor";
 
1077
 
 
1078
        Member ctorMember = FunctionObject.findSingleMethod(methods, ctorName);
 
1079
 
 
1080
        if (ctorMember == null) {
 
1081
            if (ctors.length == 1) {
 
1082
                ctorMember = ctors[0];
 
1083
            } else if (ctors.length == 2) {
 
1084
                if (ctors[0].getParameterTypes().length == 0)
 
1085
                    ctorMember = ctors[1];
 
1086
                else if (ctors[1].getParameterTypes().length == 0)
 
1087
                    ctorMember = ctors[0];
 
1088
            }
 
1089
            if (ctorMember == null) {
 
1090
                throw Context.reportRuntimeError1(
 
1091
                          "msg.ctor.multiple.parms", clazz.getName());
 
1092
            }
 
1093
        }
 
1094
 
 
1095
        FunctionObject ctor = new FunctionObject(className, ctorMember, scope);
 
1096
        if (ctor.isVarArgsMethod()) {
 
1097
            throw Context.reportRuntimeError1
 
1098
                ("msg.varargs.ctor", ctorMember.getName());
 
1099
        }
 
1100
        ctor.initAsConstructor(scope, proto);
 
1101
 
 
1102
        Method finishInit = null;
 
1103
        for (int i=0; i < methods.length; i++) {
 
1104
            if (methods[i] == ctorMember) {
 
1105
                continue;
 
1106
            }
 
1107
            String name = methods[i].getName();
 
1108
            if (name.equals("finishInit")) {
 
1109
                Class[] parmTypes = methods[i].getParameterTypes();
 
1110
                if (parmTypes.length == 3 &&
 
1111
                    parmTypes[0] == ScriptRuntime.ScriptableClass &&
 
1112
                    parmTypes[1] == FunctionObject.class &&
 
1113
                    parmTypes[2] == ScriptRuntime.ScriptableClass &&
 
1114
                    Modifier.isStatic(methods[i].getModifiers()))
 
1115
                {
 
1116
                    finishInit = methods[i];
 
1117
                    continue;
 
1118
                }
 
1119
            }
 
1120
            // ignore any compiler generated methods.
 
1121
            if (name.indexOf('$') != -1)
 
1122
                continue;
 
1123
            if (name.equals(ctorName))
 
1124
                continue;
 
1125
 
 
1126
            String prefix = null;
 
1127
            if (name.startsWith(functionPrefix)) {
 
1128
                prefix = functionPrefix;
 
1129
            } else if (name.startsWith(staticFunctionPrefix)) {
 
1130
                prefix = staticFunctionPrefix;
 
1131
                if (!Modifier.isStatic(methods[i].getModifiers())) {
 
1132
                    throw Context.reportRuntimeError(
 
1133
                        "jsStaticFunction must be used with static method.");
 
1134
                }
 
1135
            } else if (name.startsWith(getterPrefix)) {
 
1136
                prefix = getterPrefix;
 
1137
            } else if (name.startsWith(setterPrefix)) {
 
1138
                prefix = setterPrefix;
 
1139
            } else {
 
1140
                continue;
 
1141
            }
 
1142
            name = name.substring(prefix.length());
 
1143
            if (prefix == setterPrefix)
 
1144
                continue;   // deal with set when we see get
 
1145
            if (prefix == getterPrefix) {
 
1146
                if (!(proto instanceof ScriptableObject)) {
 
1147
                    throw Context.reportRuntimeError2(
 
1148
                        "msg.extend.scriptable",
 
1149
                        proto.getClass().toString(), name);
 
1150
                }
 
1151
                Method setter = FunctionObject.findSingleMethod(
 
1152
                                    methods,
 
1153
                                    setterPrefix + name);
 
1154
                int attr = ScriptableObject.PERMANENT |
 
1155
                           ScriptableObject.DONTENUM  |
 
1156
                           (setter != null ? 0
 
1157
                                           : ScriptableObject.READONLY);
 
1158
                ((ScriptableObject) proto).defineProperty(name, null,
 
1159
                                                          methods[i], setter,
 
1160
                                                          attr);
 
1161
                continue;
 
1162
            }
 
1163
 
 
1164
            FunctionObject f = new FunctionObject(name, methods[i], proto);
 
1165
            if (f.isVarArgsConstructor()) {
 
1166
                throw Context.reportRuntimeError1
 
1167
                    ("msg.varargs.fun", ctorMember.getName());
 
1168
            }
 
1169
            Scriptable dest = prefix == staticFunctionPrefix
 
1170
                              ? ctor
 
1171
                              : proto;
 
1172
            defineProperty(dest, name, f, DONTENUM);
 
1173
            if (sealed) {
 
1174
                f.sealObject();
 
1175
            }
 
1176
        }
 
1177
 
 
1178
        // Call user code to complete initialization if necessary.
 
1179
        if (finishInit != null) {
 
1180
            Object[] finishArgs = { scope, ctor, proto };
 
1181
            finishInit.invoke(null, finishArgs);
 
1182
        }
 
1183
 
 
1184
        // Seal the object if necessary.
 
1185
        if (sealed) {
 
1186
            ctor.sealObject();
 
1187
            if (proto instanceof ScriptableObject) {
 
1188
                ((ScriptableObject) proto).sealObject();
 
1189
            }
 
1190
        }
 
1191
 
 
1192
        return ctor;
 
1193
    }
 
1194
 
 
1195
    /**
 
1196
     * Define a JavaScript property.
 
1197
     *
 
1198
     * Creates the property with an initial value and sets its attributes.
 
1199
     *
 
1200
     * @param propertyName the name of the property to define.
 
1201
     * @param value the initial value of the property
 
1202
     * @param attributes the attributes of the JavaScript property
 
1203
     * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object)
 
1204
     */
 
1205
    public void defineProperty(String propertyName, Object value,
 
1206
                               int attributes)
 
1207
    {
 
1208
        checkNotSealed(propertyName, 0);
 
1209
        put(propertyName, this, value);
 
1210
        setAttributes(propertyName, attributes);
 
1211
    }
 
1212
 
 
1213
    /**
 
1214
     * Utility method to add properties to arbitrary Scriptable object.
 
1215
     * If destination is instance of ScriptableObject, calls
 
1216
     * defineProperty there, otherwise calls put in destination
 
1217
     * ignoring attributes
 
1218
     */
 
1219
    public static void defineProperty(Scriptable destination,
 
1220
                                      String propertyName, Object value,
 
1221
                                      int attributes)
 
1222
    {
 
1223
        if (!(destination instanceof ScriptableObject)) {
 
1224
            destination.put(propertyName, destination, value);
 
1225
            return;
 
1226
        }
 
1227
        ScriptableObject so = (ScriptableObject)destination;
 
1228
        so.defineProperty(propertyName, value, attributes);
 
1229
    }
 
1230
 
 
1231
    /**
 
1232
     * Utility method to add properties to arbitrary Scriptable object.
 
1233
     * If destination is instance of ScriptableObject, calls
 
1234
     * defineProperty there, otherwise calls put in destination
 
1235
     * ignoring attributes
 
1236
     */
 
1237
    public static void defineConstProperty(Scriptable destination,
 
1238
                                           String propertyName)
 
1239
    {
 
1240
        if (destination instanceof ConstProperties) {
 
1241
            ConstProperties cp = (ConstProperties)destination;
 
1242
            cp.defineConst(propertyName, destination);
 
1243
        } else
 
1244
            defineProperty(destination, propertyName, Undefined.instance, CONST);
 
1245
    }
 
1246
 
 
1247
    /**
 
1248
     * Define a JavaScript property with getter and setter side effects.
 
1249
     *
 
1250
     * If the setter is not found, the attribute READONLY is added to
 
1251
     * the given attributes. <p>
 
1252
     *
 
1253
     * The getter must be a method with zero parameters, and the setter, if
 
1254
     * found, must be a method with one parameter.<p>
 
1255
     *
 
1256
     * @param propertyName the name of the property to define. This name
 
1257
     *                    also affects the name of the setter and getter
 
1258
     *                    to search for. If the propertyId is "foo", then
 
1259
     *                    <code>clazz</code> will be searched for "getFoo"
 
1260
     *                    and "setFoo" methods.
 
1261
     * @param clazz the Java class to search for the getter and setter
 
1262
     * @param attributes the attributes of the JavaScript property
 
1263
     * @see org.mozilla.javascript.Scriptable#put(String, Scriptable, Object)
 
1264
     */
 
1265
    public void defineProperty(String propertyName, Class clazz,
 
1266
                               int attributes)
 
1267
    {
 
1268
        int length = propertyName.length();
 
1269
        if (length == 0) throw new IllegalArgumentException();
 
1270
        char[] buf = new char[3 + length];
 
1271
        propertyName.getChars(0, length, buf, 3);
 
1272
        buf[3] = Character.toUpperCase(buf[3]);
 
1273
        buf[0] = 'g';
 
1274
        buf[1] = 'e';
 
1275
        buf[2] = 't';
 
1276
        String getterName = new String(buf);
 
1277
        buf[0] = 's';
 
1278
        String setterName = new String(buf);
 
1279
 
 
1280
        Method[] methods = FunctionObject.getMethodList(clazz);
 
1281
        Method getter = FunctionObject.findSingleMethod(methods, getterName);
 
1282
        Method setter = FunctionObject.findSingleMethod(methods, setterName);
 
1283
        if (setter == null)
 
1284
            attributes |= ScriptableObject.READONLY;
 
1285
        defineProperty(propertyName, null, getter,
 
1286
                       setter == null ? null : setter, attributes);
 
1287
    }
 
1288
 
 
1289
    /**
 
1290
     * Define a JavaScript property.
 
1291
     *
 
1292
     * Use this method only if you wish to define getters and setters for
 
1293
     * a given property in a ScriptableObject. To create a property without
 
1294
     * special getter or setter side effects, use
 
1295
     * <code>defineProperty(String,int)</code>.
 
1296
     *
 
1297
     * If <code>setter</code> is null, the attribute READONLY is added to
 
1298
     * the given attributes.<p>
 
1299
     *
 
1300
     * Several forms of getters or setters are allowed. In all cases the
 
1301
     * type of the value parameter can be any one of the following types:
 
1302
     * Object, String, boolean, Scriptable, byte, short, int, long, float,
 
1303
     * or double. The runtime will perform appropriate conversions based
 
1304
     * upon the type of the parameter (see description in FunctionObject).
 
1305
     * The first forms are nonstatic methods of the class referred to
 
1306
     * by 'this':
 
1307
     * <pre>
 
1308
     * Object getFoo();
 
1309
     * void setFoo(SomeType value);</pre>
 
1310
     * Next are static methods that may be of any class; the object whose
 
1311
     * property is being accessed is passed in as an extra argument:
 
1312
     * <pre>
 
1313
     * static Object getFoo(Scriptable obj);
 
1314
     * static void setFoo(Scriptable obj, SomeType value);</pre>
 
1315
     * Finally, it is possible to delegate to another object entirely using
 
1316
     * the <code>delegateTo</code> parameter. In this case the methods are
 
1317
     * nonstatic methods of the class delegated to, and the object whose
 
1318
     * property is being accessed is passed in as an extra argument:
 
1319
     * <pre>
 
1320
     * Object getFoo(Scriptable obj);
 
1321
     * void setFoo(Scriptable obj, SomeType value);</pre>
 
1322
     *
 
1323
     * @param propertyName the name of the property to define.
 
1324
     * @param delegateTo an object to call the getter and setter methods on,
 
1325
     *                   or null, depending on the form used above.
 
1326
     * @param getter the method to invoke to get the value of the property
 
1327
     * @param setter the method to invoke to set the value of the property
 
1328
     * @param attributes the attributes of the JavaScript property
 
1329
     */
 
1330
    public void defineProperty(String propertyName, Object delegateTo,
 
1331
                               Method getter, Method setter, int attributes)
 
1332
    {
 
1333
        MemberBox getterBox = null;
 
1334
        if (getter != null) {
 
1335
            getterBox = new MemberBox(getter);
 
1336
 
 
1337
            boolean delegatedForm;
 
1338
            if (!Modifier.isStatic(getter.getModifiers())) {
 
1339
                delegatedForm = (delegateTo != null);
 
1340
                getterBox.delegateTo = delegateTo;
 
1341
            } else {
 
1342
                delegatedForm = true;
 
1343
                // Ignore delegateTo for static getter but store
 
1344
                // non-null delegateTo indicator.
 
1345
                getterBox.delegateTo = Void.TYPE;
 
1346
            }
 
1347
 
 
1348
            String errorId = null;
 
1349
            Class[] parmTypes = getter.getParameterTypes();
 
1350
            if (parmTypes.length == 0) {
 
1351
                if (delegatedForm) {
 
1352
                    errorId = "msg.obj.getter.parms";
 
1353
                }
 
1354
            } else if (parmTypes.length == 1) {
 
1355
                Object argType = parmTypes[0];
 
1356
                // Allow ScriptableObject for compatibility
 
1357
                if (!(argType == ScriptRuntime.ScriptableClass ||
 
1358
                      argType == ScriptRuntime.ScriptableObjectClass))
 
1359
                {
 
1360
                    errorId = "msg.bad.getter.parms";
 
1361
                } else if (!delegatedForm) {
 
1362
                    errorId = "msg.bad.getter.parms";
 
1363
                }
 
1364
            } else {
 
1365
                errorId = "msg.bad.getter.parms";
 
1366
            }
 
1367
            if (errorId != null) {
 
1368
                throw Context.reportRuntimeError1(errorId, getter.toString());
 
1369
            }
 
1370
        }
 
1371
 
 
1372
        MemberBox setterBox = null;
 
1373
        if (setter != null) {
 
1374
            if (setter.getReturnType() != Void.TYPE)
 
1375
                throw Context.reportRuntimeError1("msg.setter.return",
 
1376
                                                  setter.toString());
 
1377
 
 
1378
            setterBox = new MemberBox(setter);
 
1379
 
 
1380
            boolean delegatedForm;
 
1381
            if (!Modifier.isStatic(setter.getModifiers())) {
 
1382
                delegatedForm = (delegateTo != null);
 
1383
                setterBox.delegateTo = delegateTo;
 
1384
            } else {
 
1385
                delegatedForm = true;
 
1386
                // Ignore delegateTo for static setter but store
 
1387
                // non-null delegateTo indicator.
 
1388
                setterBox.delegateTo = Void.TYPE;
 
1389
            }
 
1390
 
 
1391
            String errorId = null;
 
1392
            Class[] parmTypes = setter.getParameterTypes();
 
1393
            if (parmTypes.length == 1) {
 
1394
                if (delegatedForm) {
 
1395
                    errorId = "msg.setter2.expected";
 
1396
                }
 
1397
            } else if (parmTypes.length == 2) {
 
1398
                Object argType = parmTypes[0];
 
1399
                // Allow ScriptableObject for compatibility
 
1400
                if (!(argType == ScriptRuntime.ScriptableClass ||
 
1401
                      argType == ScriptRuntime.ScriptableObjectClass))
 
1402
                {
 
1403
                    errorId = "msg.setter2.parms";
 
1404
                } else if (!delegatedForm) {
 
1405
                    errorId = "msg.setter1.parms";
 
1406
                }
 
1407
            } else {
 
1408
                errorId = "msg.setter.parms";
 
1409
            }
 
1410
            if (errorId != null) {
 
1411
                throw Context.reportRuntimeError1(errorId, setter.toString());
 
1412
            }
 
1413
        }
 
1414
 
 
1415
        GetterSlot gslot = (GetterSlot)getSlot(propertyName, 0,
 
1416
                                               SLOT_MODIFY_GETTER_SETTER);
 
1417
        gslot.setAttributes(attributes);
 
1418
        gslot.getter = getterBox;
 
1419
        gslot.setter = setterBox;
 
1420
    }
 
1421
 
 
1422
    /**
 
1423
     * Search for names in a class, adding the resulting methods
 
1424
     * as properties.
 
1425
     *
 
1426
     * <p> Uses reflection to find the methods of the given names. Then
 
1427
     * FunctionObjects are constructed from the methods found, and
 
1428
     * are added to this object as properties with the given names.
 
1429
     *
 
1430
     * @param names the names of the Methods to add as function properties
 
1431
     * @param clazz the class to search for the Methods
 
1432
     * @param attributes the attributes of the new properties
 
1433
     * @see org.mozilla.javascript.FunctionObject
 
1434
     */
 
1435
    public void defineFunctionProperties(String[] names, Class clazz,
 
1436
                                         int attributes)
 
1437
    {
 
1438
        Method[] methods = FunctionObject.getMethodList(clazz);
 
1439
        for (int i=0; i < names.length; i++) {
 
1440
            String name = names[i];
 
1441
            Method m = FunctionObject.findSingleMethod(methods, name);
 
1442
            if (m == null) {
 
1443
                throw Context.reportRuntimeError2(
 
1444
                    "msg.method.not.found", name, clazz.getName());
 
1445
            }
 
1446
            FunctionObject f = new FunctionObject(name, m, this);
 
1447
            defineProperty(name, f, attributes);
 
1448
        }
 
1449
    }
 
1450
 
 
1451
    /**
 
1452
     * Get the Object.prototype property.
 
1453
     * See ECMA 15.2.4.
 
1454
     */
 
1455
    public static Scriptable getObjectPrototype(Scriptable scope) {
 
1456
        return getClassPrototype(scope, "Object");
 
1457
    }
 
1458
 
 
1459
    /**
 
1460
     * Get the Function.prototype property.
 
1461
     * See ECMA 15.3.4.
 
1462
     */
 
1463
    public static Scriptable getFunctionPrototype(Scriptable scope) {
 
1464
        return getClassPrototype(scope, "Function");
 
1465
    }
 
1466
 
 
1467
    /**
 
1468
     * Get the prototype for the named class.
 
1469
     *
 
1470
     * For example, <code>getClassPrototype(s, "Date")</code> will first
 
1471
     * walk up the parent chain to find the outermost scope, then will
 
1472
     * search that scope for the Date constructor, and then will
 
1473
     * return Date.prototype. If any of the lookups fail, or
 
1474
     * the prototype is not a JavaScript object, then null will
 
1475
     * be returned.
 
1476
     *
 
1477
     * @param scope an object in the scope chain
 
1478
     * @param className the name of the constructor
 
1479
     * @return the prototype for the named class, or null if it
 
1480
     *         cannot be found.
 
1481
     */
 
1482
    public static Scriptable getClassPrototype(Scriptable scope,
 
1483
                                               String className)
 
1484
    {
 
1485
        scope = getTopLevelScope(scope);
 
1486
        Object ctor = getProperty(scope, className);
 
1487
        Object proto;
 
1488
        if (ctor instanceof BaseFunction) {
 
1489
            proto = ((BaseFunction)ctor).getPrototypeProperty();
 
1490
        } else if (ctor instanceof Scriptable) {
 
1491
            Scriptable ctorObj = (Scriptable)ctor;
 
1492
            proto = ctorObj.get("prototype", ctorObj);
 
1493
        } else {
 
1494
            return null;
 
1495
        }
 
1496
        if (proto instanceof Scriptable) {
 
1497
            return (Scriptable)proto;
 
1498
        }
 
1499
        return null;
 
1500
    }
 
1501
 
 
1502
    /**
 
1503
     * Get the global scope.
 
1504
     *
 
1505
     * <p>Walks the parent scope chain to find an object with a null
 
1506
     * parent scope (the global object).
 
1507
     *
 
1508
     * @param obj a JavaScript object
 
1509
     * @return the corresponding global scope
 
1510
     */
 
1511
    public static Scriptable getTopLevelScope(Scriptable obj)
 
1512
    {
 
1513
        for (;;) {
 
1514
            Scriptable parent = obj.getParentScope();
 
1515
            if (parent == null) {
 
1516
                return obj;
 
1517
            }
 
1518
            obj = parent;
 
1519
        }
 
1520
    }
 
1521
 
 
1522
    // APPJET
 
1523
    public static Scriptable getVeryTopLevelScope(Scriptable obj) {
 
1524
        return ScriptRuntime.getLibraryScopeOrNull(obj);
 
1525
    }
 
1526
    
 
1527
    /**
 
1528
     * Seal this object.
 
1529
     *
 
1530
     * A sealed object may not have properties added or removed. Once
 
1531
     * an object is sealed it may not be unsealed.
 
1532
     *
 
1533
     * @since 1.4R3
 
1534
     */
 
1535
    public synchronized void sealObject() {
 
1536
        if (count >= 0) {
 
1537
            count = ~count;
 
1538
        }
 
1539
    }
 
1540
 
 
1541
    /**
 
1542
     * Return true if this object is sealed.
 
1543
     *
 
1544
     * It is an error to attempt to add or remove properties to
 
1545
     * a sealed object.
 
1546
     *
 
1547
     * @return true if sealed, false otherwise.
 
1548
     * @since 1.4R3
 
1549
     */
 
1550
    public final boolean isSealed() {
 
1551
        return count < 0;
 
1552
    }
 
1553
 
 
1554
    private void checkNotSealed(String name, int index)
 
1555
    {
 
1556
        if (!isSealed())
 
1557
            return;
 
1558
 
 
1559
        String str = (name != null) ? name : Integer.toString(index);
 
1560
        throw Context.reportRuntimeError1("msg.modify.sealed", str);
 
1561
    }
 
1562
 
 
1563
    /**
 
1564
     * Gets a named property from an object or any object in its prototype chain.
 
1565
     * <p>
 
1566
     * Searches the prototype chain for a property named <code>name</code>.
 
1567
     * <p>
 
1568
     * @param obj a JavaScript object
 
1569
     * @param name a property name
 
1570
     * @return the value of a property with name <code>name</code> found in
 
1571
     *         <code>obj</code> or any object in its prototype chain, or
 
1572
     *         <code>Scriptable.NOT_FOUND</code> if not found
 
1573
     * @since 1.5R2
 
1574
     */
 
1575
    public static Object getProperty(Scriptable obj, String name)
 
1576
    {
 
1577
        Scriptable start = obj;
 
1578
        Object result;
 
1579
        do {
 
1580
            result = obj.get(name, start);
 
1581
            if (result != Scriptable.NOT_FOUND)
 
1582
                break;
 
1583
            obj = obj.getPrototype();
 
1584
        } while (obj != null);
 
1585
        return result;
 
1586
    }
 
1587
 
 
1588
    /**
 
1589
     * Gets an indexed property from an object or any object in its prototype chain.
 
1590
     * <p>
 
1591
     * Searches the prototype chain for a property with integral index
 
1592
     * <code>index</code>. Note that if you wish to look for properties with numerical
 
1593
     * but non-integral indicies, you should use getProperty(Scriptable,String) with
 
1594
     * the string value of the index.
 
1595
     * <p>
 
1596
     * @param obj a JavaScript object
 
1597
     * @param index an integral index
 
1598
     * @return the value of a property with index <code>index</code> found in
 
1599
     *         <code>obj</code> or any object in its prototype chain, or
 
1600
     *         <code>Scriptable.NOT_FOUND</code> if not found
 
1601
     * @since 1.5R2
 
1602
     */
 
1603
    public static Object getProperty(Scriptable obj, int index)
 
1604
    {
 
1605
        Scriptable start = obj;
 
1606
        Object result;
 
1607
        do {
 
1608
            result = obj.get(index, start);
 
1609
            if (result != Scriptable.NOT_FOUND)
 
1610
                break;
 
1611
            obj = obj.getPrototype();
 
1612
        } while (obj != null);
 
1613
        return result;
 
1614
    }
 
1615
 
 
1616
    /**
 
1617
     * Returns whether a named property is defined in an object or any object
 
1618
     * in its prototype chain.
 
1619
     * <p>
 
1620
     * Searches the prototype chain for a property named <code>name</code>.
 
1621
     * <p>
 
1622
     * @param obj a JavaScript object
 
1623
     * @param name a property name
 
1624
     * @return the true if property was found
 
1625
     * @since 1.5R2
 
1626
     */
 
1627
    public static boolean hasProperty(Scriptable obj, String name)
 
1628
    {
 
1629
        return null != getBase(obj, name);
 
1630
    }
 
1631
 
 
1632
    /**
 
1633
     * If hasProperty(obj, name) would return true, then if the property that
 
1634
     * was found is compatible with the new property, this method just returns.
 
1635
     * If the property is not compatible, then an exception is thrown.
 
1636
     *
 
1637
     * A property redefinition is incompatible if the first definition was a
 
1638
     * const declaration or if this one is.  They are compatible only if neither
 
1639
     * was const.
 
1640
     */
 
1641
    public static void redefineProperty(Scriptable obj, String name,
 
1642
                                        boolean isConst)
 
1643
    {
 
1644
        Scriptable base = getBase(obj, name);
 
1645
        if (base == null)
 
1646
            return;
 
1647
        if (base instanceof ConstProperties) {
 
1648
            ConstProperties cp = (ConstProperties)base;
 
1649
 
 
1650
            if (cp.isConst(name))
 
1651
                throw Context.reportRuntimeError1("msg.const.redecl", name);
 
1652
        }
 
1653
        if (isConst)
 
1654
            throw Context.reportRuntimeError1("msg.var.redecl", name);
 
1655
    }
 
1656
    /**
 
1657
     * Returns whether an indexed property is defined in an object or any object
 
1658
     * in its prototype chain.
 
1659
     * <p>
 
1660
     * Searches the prototype chain for a property with index <code>index</code>.
 
1661
     * <p>
 
1662
     * @param obj a JavaScript object
 
1663
     * @param index a property index
 
1664
     * @return the true if property was found
 
1665
     * @since 1.5R2
 
1666
     */
 
1667
    public static boolean hasProperty(Scriptable obj, int index)
 
1668
    {
 
1669
        return null != getBase(obj, index);
 
1670
    }
 
1671
 
 
1672
    /**
 
1673
     * Puts a named property in an object or in an object in its prototype chain.
 
1674
     * <p>
 
1675
     * Searches for the named property in the prototype chain. If it is found,
 
1676
     * the value of the property in <code>obj</code> is changed through a call
 
1677
     * to {@link Scriptable#put(String, Scriptable, Object)} on the
 
1678
     * prototype passing <code>obj</code> as the <code>start</code> argument.
 
1679
     * This allows the prototype to veto the property setting in case the
 
1680
     * prototype defines the property with [[ReadOnly]] attribute. If the
 
1681
     * property is not found, it is added in <code>obj</code>.
 
1682
     * @param obj a JavaScript object
 
1683
     * @param name a property name
 
1684
     * @param value any JavaScript value accepted by Scriptable.put
 
1685
     * @since 1.5R2
 
1686
     */
 
1687
    public static void putProperty(Scriptable obj, String name, Object value)
 
1688
    {
 
1689
        Scriptable base = getBase(obj, name);
 
1690
        if (base == null)
 
1691
            base = obj;
 
1692
        base.put(name, obj, value);
 
1693
    }
 
1694
 
 
1695
    /**
 
1696
     * Puts a named property in an object or in an object in its prototype chain.
 
1697
     * <p>
 
1698
     * Searches for the named property in the prototype chain. If it is found,
 
1699
     * the value of the property in <code>obj</code> is changed through a call
 
1700
     * to {@link Scriptable#put(String, Scriptable, Object)} on the
 
1701
     * prototype passing <code>obj</code> as the <code>start</code> argument.
 
1702
     * This allows the prototype to veto the property setting in case the
 
1703
     * prototype defines the property with [[ReadOnly]] attribute. If the
 
1704
     * property is not found, it is added in <code>obj</code>.
 
1705
     * @param obj a JavaScript object
 
1706
     * @param name a property name
 
1707
     * @param value any JavaScript value accepted by Scriptable.put
 
1708
     * @since 1.5R2
 
1709
     */
 
1710
    public static void putConstProperty(Scriptable obj, String name, Object value)
 
1711
    {
 
1712
        Scriptable base = getBase(obj, name);
 
1713
        if (base == null)
 
1714
            base = obj;
 
1715
        if (base instanceof ConstProperties)
 
1716
            ((ConstProperties)base).putConst(name, obj, value);
 
1717
    }
 
1718
 
 
1719
    /**
 
1720
     * Puts an indexed property in an object or in an object in its prototype chain.
 
1721
     * <p>
 
1722
     * Searches for the indexed property in the prototype chain. If it is found,
 
1723
     * the value of the property in <code>obj</code> is changed through a call
 
1724
     * to {@link Scriptable#put(int, Scriptable, Object)} on the prototype
 
1725
     * passing <code>obj</code> as the <code>start</code> argument. This allows
 
1726
     * the prototype to veto the property setting in case the prototype defines
 
1727
     * the property with [[ReadOnly]] attribute. If the property is not found, 
 
1728
     * it is added in <code>obj</code>.
 
1729
     * @param obj a JavaScript object
 
1730
     * @param index a property index
 
1731
     * @param value any JavaScript value accepted by Scriptable.put
 
1732
     * @since 1.5R2
 
1733
     */
 
1734
    public static void putProperty(Scriptable obj, int index, Object value)
 
1735
    {
 
1736
        Scriptable base = getBase(obj, index);
 
1737
        if (base == null)
 
1738
            base = obj;
 
1739
        base.put(index, obj, value);
 
1740
    }
 
1741
 
 
1742
    /**
 
1743
     * Removes the property from an object or its prototype chain.
 
1744
     * <p>
 
1745
     * Searches for a property with <code>name</code> in obj or
 
1746
     * its prototype chain. If it is found, the object's delete
 
1747
     * method is called.
 
1748
     * @param obj a JavaScript object
 
1749
     * @param name a property name
 
1750
     * @return true if the property doesn't exist or was successfully removed
 
1751
     * @since 1.5R2
 
1752
     */
 
1753
    public static boolean deleteProperty(Scriptable obj, String name)
 
1754
    {
 
1755
        Scriptable base = getBase(obj, name);
 
1756
        if (base == null)
 
1757
            return true;
 
1758
        base.delete(name);
 
1759
        return !base.has(name, obj);
 
1760
    }
 
1761
 
 
1762
    /**
 
1763
     * Removes the property from an object or its prototype chain.
 
1764
     * <p>
 
1765
     * Searches for a property with <code>index</code> in obj or
 
1766
     * its prototype chain. If it is found, the object's delete
 
1767
     * method is called.
 
1768
     * @param obj a JavaScript object
 
1769
     * @param index a property index
 
1770
     * @return true if the property doesn't exist or was successfully removed
 
1771
     * @since 1.5R2
 
1772
     */
 
1773
    public static boolean deleteProperty(Scriptable obj, int index)
 
1774
    {
 
1775
        Scriptable base = getBase(obj, index);
 
1776
        if (base == null)
 
1777
            return true;
 
1778
        base.delete(index);
 
1779
        return !base.has(index, obj);
 
1780
    }
 
1781
 
 
1782
    /**
 
1783
     * Returns an array of all ids from an object and its prototypes.
 
1784
     * <p>
 
1785
     * @param obj a JavaScript object
 
1786
     * @return an array of all ids from all object in the prototype chain.
 
1787
     *         If a given id occurs multiple times in the prototype chain,
 
1788
     *         it will occur only once in this list.
 
1789
     * @since 1.5R2
 
1790
     */
 
1791
    public static Object[] getPropertyIds(Scriptable obj)
 
1792
    {
 
1793
        if (obj == null) {
 
1794
            return ScriptRuntime.emptyArgs;
 
1795
        }
 
1796
        Object[] result = obj.getIds();
 
1797
        ObjToIntMap map = null;
 
1798
        for (;;) {
 
1799
            obj = obj.getPrototype();
 
1800
            if (obj == null) {
 
1801
                break;
 
1802
            }
 
1803
            Object[] ids = obj.getIds();
 
1804
            if (ids.length == 0) {
 
1805
                continue;
 
1806
            }
 
1807
            if (map == null) {
 
1808
                if (result.length == 0) {
 
1809
                    result = ids;
 
1810
                    continue;
 
1811
                }
 
1812
                map = new ObjToIntMap(result.length + ids.length);
 
1813
                for (int i = 0; i != result.length; ++i) {
 
1814
                    map.intern(result[i]);
 
1815
                }
 
1816
                result = null; // Allow to GC the result
 
1817
            }
 
1818
            for (int i = 0; i != ids.length; ++i) {
 
1819
                map.intern(ids[i]);
 
1820
            }
 
1821
        }
 
1822
        if (map != null) {
 
1823
            result = map.getKeys();
 
1824
        }
 
1825
        return result;
 
1826
    }
 
1827
 
 
1828
    /**
 
1829
     * Call a method of an object.
 
1830
     * @param obj the JavaScript object
 
1831
     * @param methodName the name of the function property
 
1832
     * @param args the arguments for the call
 
1833
     *
 
1834
     * @see Context#getCurrentContext()
 
1835
     */
 
1836
    public static Object callMethod(Scriptable obj, String methodName,
 
1837
                                    Object[] args)
 
1838
    {
 
1839
        return callMethod(null, obj, methodName, args);
 
1840
    }
 
1841
 
 
1842
    /**
 
1843
     * Call a method of an object.
 
1844
     * @param cx the Context object associated with the current thread.
 
1845
     * @param obj the JavaScript object
 
1846
     * @param methodName the name of the function property
 
1847
     * @param args the arguments for the call
 
1848
     */
 
1849
    public static Object callMethod(Context cx, Scriptable obj,
 
1850
                                    String methodName,
 
1851
                                    Object[] args)
 
1852
    {
 
1853
        Object funObj = getProperty(obj, methodName);
 
1854
        if (!(funObj instanceof Function)) {
 
1855
            throw ScriptRuntime.notFunctionError(obj, methodName);
 
1856
        }
 
1857
        Function fun = (Function)funObj;
 
1858
        // XXX: What should be the scope when calling funObj?
 
1859
        // The following favor scope stored in the object on the assumption
 
1860
        // that is more useful especially under dynamic scope setup.
 
1861
        // An alternative is to check for dynamic scope flag
 
1862
        // and use ScriptableObject.getTopLevelScope(fun) if the flag is not
 
1863
        // set. But that require access to Context and messy code
 
1864
        // so for now it is not checked.
 
1865
        Scriptable scope = ScriptableObject.getTopLevelScope(obj);
 
1866
        if (cx != null) {
 
1867
            return fun.call(cx, scope, obj, args);
 
1868
        } else {
 
1869
            return Context.call(null, fun, scope, obj, args);
 
1870
        }
 
1871
    }
 
1872
 
 
1873
    private static Scriptable getBase(Scriptable obj, String name)
 
1874
    {
 
1875
        do {
 
1876
            if (obj.has(name, obj))
 
1877
                break;
 
1878
            obj = obj.getPrototype();
 
1879
        } while(obj != null);
 
1880
        return obj;
 
1881
    }
 
1882
 
 
1883
    private static Scriptable getBase(Scriptable obj, int index)
 
1884
    {
 
1885
        do {
 
1886
            if (obj.has(index, obj))
 
1887
                break;
 
1888
            obj = obj.getPrototype();
 
1889
        } while(obj != null);
 
1890
        return obj;
 
1891
    }
 
1892
 
 
1893
    /**
 
1894
     * Get arbitrary application-specific value associated with this object.
 
1895
     * @param key key object to select particular value.
 
1896
     * @see #associateValue(Object key, Object value)
 
1897
     */
 
1898
    public final Object getAssociatedValue(Object key)
 
1899
    {
 
1900
        Hashtable h = associatedValues;
 
1901
        if (h == null)
 
1902
            return null;
 
1903
        return h.get(key);
 
1904
    }
 
1905
 
 
1906
    /**
 
1907
     * Get arbitrary application-specific value associated with the top scope
 
1908
     * of the given scope.
 
1909
     * The method first calls {@link #getTopLevelScope(Scriptable scope)}
 
1910
     * and then searches the prototype chain of the top scope for the first
 
1911
     * object containing the associated value with the given key.
 
1912
     *
 
1913
     * @param scope the starting scope.
 
1914
     * @param key key object to select particular value.
 
1915
     * @see #getAssociatedValue(Object key)
 
1916
     */
 
1917
    public static Object getTopScopeValue(Scriptable scope, Object key)
 
1918
    {
 
1919
        scope = ScriptableObject.getTopLevelScope(scope);
 
1920
        for (;;) {
 
1921
            if (scope instanceof ScriptableObject) {
 
1922
                ScriptableObject so = (ScriptableObject)scope;
 
1923
                Object value = so.getAssociatedValue(key);
 
1924
                if (value != null) {
 
1925
                    return value;
 
1926
                }
 
1927
            }
 
1928
            scope = scope.getPrototype();
 
1929
            if (scope == null) {
 
1930
                return null;
 
1931
            }
 
1932
        }
 
1933
    }
 
1934
 
 
1935
    /**
 
1936
     * Associate arbitrary application-specific value with this object.
 
1937
     * Value can only be associated with the given object and key only once.
 
1938
     * The method ignores any subsequent attempts to change the already
 
1939
     * associated value.
 
1940
     * <p> The associated values are not serialized.
 
1941
     * @param key key object to select particular value.
 
1942
     * @param value the value to associate
 
1943
     * @return the passed value if the method is called first time for the
 
1944
     * given key or old value for any subsequent calls.
 
1945
     * @see #getAssociatedValue(Object key)
 
1946
     */
 
1947
    public final Object associateValue(Object key, Object value)
 
1948
    {
 
1949
        if (value == null) throw new IllegalArgumentException();
 
1950
        Hashtable h = associatedValues;
 
1951
        if (h == null) {
 
1952
            synchronized (this) {
 
1953
                h = associatedValues;
 
1954
                if (h == null) {
 
1955
                    h = new Hashtable();
 
1956
                    associatedValues = h;
 
1957
                }
 
1958
            }
 
1959
        }
 
1960
        return Kit.initHash(h, key, value);
 
1961
    }
 
1962
 
 
1963
    private Object getImpl(String name, int index, Scriptable start)
 
1964
    {
 
1965
        Slot slot = getSlot(name, index, SLOT_QUERY);
 
1966
        if (slot == null) {
 
1967
            return Scriptable.NOT_FOUND;
 
1968
        }
 
1969
        if (!(slot instanceof GetterSlot)) {
 
1970
            return slot.value;
 
1971
        }
 
1972
        Object getterObj = ((GetterSlot)slot).getter;
 
1973
        if (getterObj != null) {
 
1974
            if (getterObj instanceof MemberBox) {
 
1975
                MemberBox nativeGetter = (MemberBox)getterObj;
 
1976
                Object getterThis;
 
1977
                Object[] args;
 
1978
                if (nativeGetter.delegateTo == null) {
 
1979
                    getterThis = start;
 
1980
                    args = ScriptRuntime.emptyArgs;
 
1981
                } else {
 
1982
                    getterThis = nativeGetter.delegateTo;
 
1983
                    args = new Object[] { start };
 
1984
                }
 
1985
                return nativeGetter.invoke(getterThis, args);
 
1986
            } else {
 
1987
                Function f = (Function)getterObj;
 
1988
                Context cx = Context.getContext();
 
1989
                return f.call(cx, f.getParentScope(), start,
 
1990
                              ScriptRuntime.emptyArgs);
 
1991
            }
 
1992
        }
 
1993
        Object value = slot.value;
 
1994
        if (value instanceof LazilyLoadedCtor) {
 
1995
            LazilyLoadedCtor initializer = (LazilyLoadedCtor)value;
 
1996
            try {
 
1997
                initializer.init();
 
1998
            } finally {
 
1999
                value = initializer.getValue();
 
2000
                slot.value = value;
 
2001
            }
 
2002
        }
 
2003
        return value;
 
2004
    }
 
2005
 
 
2006
    /**
 
2007
     *
 
2008
     * @param name
 
2009
     * @param index
 
2010
     * @param start
 
2011
     * @param value
 
2012
     * @param constFlag EMPTY means normal put.  UNINITIALIZED_CONST means
 
2013
     * defineConstProperty.  READONLY means const initialization expression.
 
2014
     * @return false if this != start and no slot was found.  true if this == start
 
2015
     * or this != start and a READONLY slot was found.
 
2016
     */
 
2017
    private boolean putImpl(String name, int index, Scriptable start,
 
2018
                            Object value, int constFlag)
 
2019
    {
 
2020
        Slot slot;
 
2021
        if (this != start) {
 
2022
            slot = getSlot(name, index, SLOT_QUERY);
 
2023
            if (slot == null) {
 
2024
                return false;
 
2025
            }
 
2026
        } else {
 
2027
            checkNotSealed(name, index);
 
2028
            // either const hoisted declaration or initialization
 
2029
            if (constFlag != EMPTY) {
 
2030
                slot = getSlot(name, index, SLOT_MODIFY_CONST);
 
2031
                int attr = slot.getAttributes();
 
2032
                if ((attr & READONLY) == 0)
 
2033
                    throw Context.reportRuntimeError1("msg.var.redecl", name);
 
2034
                if ((attr & UNINITIALIZED_CONST) != 0) {
 
2035
                    slot.value = value;
 
2036
                    // clear the bit on const initialization
 
2037
                    if (constFlag != UNINITIALIZED_CONST)
 
2038
                        slot.setAttributes(attr & ~UNINITIALIZED_CONST);
 
2039
                }
 
2040
                return true;
 
2041
            }
 
2042
            slot = getSlot(name, index, SLOT_MODIFY);
 
2043
        }
 
2044
        if ((slot.getAttributes() & READONLY) != 0)
 
2045
            return true;
 
2046
        if (slot instanceof GetterSlot) {
 
2047
            Object setterObj = ((GetterSlot)slot).setter;
 
2048
            if (setterObj != null) {
 
2049
                Context cx = Context.getContext();
 
2050
                if (setterObj instanceof MemberBox) {
 
2051
                    MemberBox nativeSetter = (MemberBox)setterObj;
 
2052
                    Class pTypes[] = nativeSetter.argTypes;
 
2053
                    // XXX: cache tag since it is already calculated in
 
2054
                    // defineProperty ?
 
2055
                    Class valueType = pTypes[pTypes.length - 1];
 
2056
                    int tag = FunctionObject.getTypeTag(valueType);
 
2057
                    Object actualArg = FunctionObject.convertArg(cx, start,
 
2058
                                                                 value, tag);
 
2059
                    Object setterThis;
 
2060
                    Object[] args;
 
2061
                    if (nativeSetter.delegateTo == null) {
 
2062
                        setterThis = start;
 
2063
                        args = new Object[] { actualArg };
 
2064
                    } else {
 
2065
                        setterThis = nativeSetter.delegateTo;
 
2066
                        args = new Object[] { start, actualArg };
 
2067
                    }
 
2068
                    nativeSetter.invoke(setterThis, args);
 
2069
                } else {
 
2070
                    Function f = (Function)setterObj;
 
2071
                    f.call(cx, f.getParentScope(), start,
 
2072
                           new Object[] { value });
 
2073
                }
 
2074
                return true;
 
2075
            }
 
2076
        }
 
2077
        if (this == start) {
 
2078
            slot.value = value;
 
2079
            return true;
 
2080
        } else {
 
2081
            return false;
 
2082
        }
 
2083
    }
 
2084
 
 
2085
    private Slot findAttributeSlot(String name, int index, int accessType)
 
2086
    {
 
2087
        Slot slot = getSlot(name, index, accessType);
 
2088
        if (slot == null) {
 
2089
            String str = (name != null ? name : Integer.toString(index));
 
2090
            throw Context.reportRuntimeError1("msg.prop.not.found", str);
 
2091
        }
 
2092
        return slot;
 
2093
    }
 
2094
 
 
2095
    /**
 
2096
     * Locate the slot with given name or index.
 
2097
     *
 
2098
     * @param name property name or null if slot holds spare array index.
 
2099
     * @param index index or 0 if slot holds property name.
 
2100
     */
 
2101
    private Slot getSlot(String name, int index, int accessType)
 
2102
    {
 
2103
        Slot slot;
 
2104
 
 
2105
        // Query last access cache and check that it was not deleted.
 
2106
      lastAccessCheck:
 
2107
        {
 
2108
            slot = lastAccess;
 
2109
            if (name != null) {
 
2110
                if (name != slot.name)
 
2111
                    break lastAccessCheck;
 
2112
                // No String.equals here as successful slot search update
 
2113
                // name object with fresh reference of the same string.
 
2114
            } else {
 
2115
                if (slot.name != null || index != slot.indexOrHash)
 
2116
                    break lastAccessCheck;
 
2117
            }
 
2118
 
 
2119
            if (slot.wasDeleted != 0)
 
2120
                break lastAccessCheck;
 
2121
 
 
2122
            if (accessType == SLOT_MODIFY_GETTER_SETTER &&
 
2123
                !(slot instanceof GetterSlot))
 
2124
                break lastAccessCheck;
 
2125
 
 
2126
            return slot;
 
2127
        }
 
2128
 
 
2129
        slot = accessSlot(name, index, accessType);
 
2130
        if (slot != null) {
 
2131
            // Update the cache
 
2132
            lastAccess = slot;
 
2133
        }
 
2134
        return slot;
 
2135
    }
 
2136
 
 
2137
    private Slot accessSlot(String name, int index, int accessType)
 
2138
    {
 
2139
        int indexOrHash = (name != null ? name.hashCode() : index);
 
2140
 
 
2141
        if (accessType == SLOT_QUERY ||
 
2142
            accessType == SLOT_MODIFY ||
 
2143
            accessType == SLOT_MODIFY_CONST ||
 
2144
            accessType == SLOT_MODIFY_GETTER_SETTER)
 
2145
        {
 
2146
            // Check the hashtable without using synchronization
 
2147
 
 
2148
            Slot[] slotsLocalRef = slots; // Get stable local reference
 
2149
            if (slotsLocalRef == null) {
 
2150
                if (accessType == SLOT_QUERY)
 
2151
                    return null;
 
2152
            } else {
 
2153
                int tableSize = slotsLocalRef.length;
 
2154
                int slotIndex = getSlotIndex(tableSize, indexOrHash);
 
2155
                Slot slot = slotsLocalRef[slotIndex];
 
2156
                while (slot != null) {
 
2157
                    String sname = slot.name;
 
2158
                    if (sname != null) {
 
2159
                        if (sname == name)
 
2160
                            break;
 
2161
                        if (name != null && indexOrHash == slot.indexOrHash) {
 
2162
                            if (name.equals(sname)) {
 
2163
                                // This will avoid calling String.equals when
 
2164
                                // slot is accessed with same string object
 
2165
                                // next time.
 
2166
                                slot.name = name;
 
2167
                                break;
 
2168
                            }
 
2169
                        }
 
2170
                    } else if (name == null &&
 
2171
                               indexOrHash == slot.indexOrHash) {
 
2172
                        break;
 
2173
                    }
 
2174
                    slot = slot.next;
 
2175
                }
 
2176
                if (accessType == SLOT_QUERY) {
 
2177
                    return slot;
 
2178
                } else if (accessType == SLOT_MODIFY) {
 
2179
                    if (slot != null)
 
2180
                        return slot;
 
2181
                } else if (accessType == SLOT_MODIFY_GETTER_SETTER) {
 
2182
                    if (slot instanceof GetterSlot)
 
2183
                        return slot;
 
2184
                } else if (accessType == SLOT_MODIFY_CONST) {
 
2185
                    if (slot != null)
 
2186
                        return slot;
 
2187
                }
 
2188
            }
 
2189
 
 
2190
            // A new slot has to be inserted or the old has to be replaced
 
2191
            // by GetterSlot. Time to synchronize.
 
2192
 
 
2193
            synchronized (this) {
 
2194
                // Refresh local ref if another thread triggered grow
 
2195
                slotsLocalRef = slots;
 
2196
                int insertPos;
 
2197
                if (count == 0) {
 
2198
                    // Always throw away old slots if any on empty insert
 
2199
                    slotsLocalRef = new Slot[5];
 
2200
                    slots = slotsLocalRef;
 
2201
                    insertPos = getSlotIndex(slotsLocalRef.length, indexOrHash);
 
2202
                } else {
 
2203
                    int tableSize = slotsLocalRef.length;
 
2204
                    insertPos = getSlotIndex(tableSize, indexOrHash);
 
2205
                    Slot prev = slotsLocalRef[insertPos];
 
2206
                    Slot slot = prev;
 
2207
                    while (slot != null) {
 
2208
                        if (slot.indexOrHash == indexOrHash &&
 
2209
                            (slot.name == name ||
 
2210
                             (name != null && name.equals(slot.name))))
 
2211
                        {
 
2212
                            break;
 
2213
                        }
 
2214
                        prev = slot;
 
2215
                        slot = slot.next;
 
2216
                    }
 
2217
 
 
2218
                    if (slot != null) {
 
2219
                        // Another thread just added a slot with same
 
2220
                        // name/index before this one entered synchronized
 
2221
                        // block. This is a race in application code and
 
2222
                        // probably indicates bug there. But for the hashtable
 
2223
                        // implementation it is harmless with the only
 
2224
                        // complication is the need to replace the added slot
 
2225
                        // if we need GetterSlot and the old one is not.
 
2226
                        if (accessType == SLOT_MODIFY_GETTER_SETTER &&
 
2227
                            !(slot instanceof GetterSlot))
 
2228
                        {
 
2229
                            GetterSlot newSlot = new GetterSlot(name, indexOrHash,
 
2230
                                    slot.getAttributes());
 
2231
                            newSlot.value = slot.value;
 
2232
                            newSlot.next = slot.next;
 
2233
                            if (prev == slot) {
 
2234
                                slotsLocalRef[insertPos] = newSlot;
 
2235
                            } else {
 
2236
                                prev.next = newSlot;
 
2237
                            }
 
2238
                            slot.wasDeleted = (byte)1;
 
2239
                            if (slot == lastAccess) {
 
2240
                                lastAccess = REMOVED;
 
2241
                            }
 
2242
                            slot = newSlot;
 
2243
                        } else if (accessType == SLOT_MODIFY_CONST) {
 
2244
                            return null;
 
2245
                        }
 
2246
                        return slot;
 
2247
                    }
 
2248
 
 
2249
                    // Check if the table is not too full before inserting.
 
2250
                    if (4 * (count + 1) > 3 * slotsLocalRef.length) {
 
2251
                        slotsLocalRef = new Slot[slotsLocalRef.length * 2 + 1];
 
2252
                        copyTable(slots, slotsLocalRef, count);
 
2253
                        slots = slotsLocalRef;
 
2254
                        insertPos = getSlotIndex(slotsLocalRef.length,
 
2255
                                indexOrHash);
 
2256
                    }
 
2257
                }
 
2258
 
 
2259
                Slot newSlot = (accessType == SLOT_MODIFY_GETTER_SETTER
 
2260
                                ? new GetterSlot(name, indexOrHash, 0)
 
2261
                                : new Slot(name, indexOrHash, 0));
 
2262
                if (accessType == SLOT_MODIFY_CONST)
 
2263
                    newSlot.setAttributes(CONST);
 
2264
                ++count;
 
2265
                addKnownAbsentSlot(slotsLocalRef, newSlot, insertPos);
 
2266
                return newSlot;
 
2267
            }
 
2268
 
 
2269
        } else if (accessType == SLOT_REMOVE) {
 
2270
            synchronized (this) {
 
2271
                Slot[] slotsLocalRef = slots;
 
2272
                if (count != 0) {
 
2273
                    int tableSize = slots.length;
 
2274
                    int slotIndex = getSlotIndex(tableSize, indexOrHash);
 
2275
                    Slot prev = slotsLocalRef[slotIndex];
 
2276
                    Slot slot = prev;
 
2277
                    while (slot != null) {
 
2278
                        if (slot.indexOrHash == indexOrHash &&
 
2279
                            (slot.name == name ||
 
2280
                             (name != null && name.equals(slot.name))))
 
2281
                        {
 
2282
                            break;
 
2283
                        }
 
2284
                        prev = slot;
 
2285
                        slot = slot.next;
 
2286
                    }
 
2287
                    if (slot != null && (slot.getAttributes() & PERMANENT) == 0) {
 
2288
                        count--;
 
2289
                        if (prev == slot) {
 
2290
                            slotsLocalRef[slotIndex] = slot.next;
 
2291
                        } else {
 
2292
                            prev.next = slot.next;
 
2293
                        }
 
2294
                        // Mark the slot as removed to handle a case when
 
2295
                        // another thread manages to put just removed slot
 
2296
                        // into lastAccess cache.
 
2297
                        slot.wasDeleted = (byte)1;
 
2298
                        if (slot == lastAccess) {
 
2299
                            lastAccess = REMOVED;
 
2300
                        }
 
2301
                    }
 
2302
                }
 
2303
            }
 
2304
            return null;
 
2305
 
 
2306
        } else {
 
2307
            throw Kit.codeBug();
 
2308
        }
 
2309
    }
 
2310
 
 
2311
    private static int getSlotIndex(int tableSize, int indexOrHash)
 
2312
    {
 
2313
        return (indexOrHash & 0x7fffffff) % tableSize;
 
2314
    }
 
2315
 
 
2316
    // Must be inside synchronized (this)
 
2317
    private static void copyTable(Slot[] slots, Slot[] newSlots, int count)
 
2318
    {
 
2319
        if (count == 0) throw Kit.codeBug();
 
2320
 
 
2321
        int tableSize = newSlots.length;
 
2322
        int i = slots.length;
 
2323
        for (;;) {
 
2324
            --i;
 
2325
            Slot slot = slots[i];
 
2326
            while (slot != null) {
 
2327
                int insertPos = getSlotIndex(tableSize, slot.indexOrHash);
 
2328
                Slot next = slot.next;
 
2329
                addKnownAbsentSlot(newSlots, slot, insertPos);
 
2330
                slot.next = null;
 
2331
                slot = next;
 
2332
                if (--count == 0)
 
2333
                    return;
 
2334
            }
 
2335
        }
 
2336
    }
 
2337
 
 
2338
    /**
 
2339
     * Add slot with keys that are known to absent from the table.
 
2340
     * This is an optimization to use when inserting into empty table,
 
2341
     * after table growth or during deserialization.
 
2342
     */
 
2343
    private static void addKnownAbsentSlot(Slot[] slots, Slot slot, int insertPos)
 
2344
    {
 
2345
        if (slots[insertPos] == null) {
 
2346
            slots[insertPos] = slot;
 
2347
        } else {
 
2348
            Slot prev = slots[insertPos];
 
2349
            while (prev.next != null) {
 
2350
                prev = prev.next;
 
2351
            }
 
2352
            prev.next = slot;
 
2353
        }
 
2354
    }
 
2355
 
 
2356
    Object[] getIds(boolean getAll) {
 
2357
        Slot[] s = slots;
 
2358
        Object[] a = ScriptRuntime.emptyArgs;
 
2359
        if (s == null)
 
2360
            return a;
 
2361
        int c = 0;
 
2362
        for (int i=0; i < s.length; i++) {
 
2363
            Slot slot = s[i];
 
2364
            while (slot != null) {
 
2365
                if (getAll || (slot.getAttributes() & DONTENUM) == 0) {
 
2366
                    if (c == 0)
 
2367
                        a = new Object[s.length];
 
2368
                    a[c++] = (slot.name != null ? (Object) slot.name
 
2369
                              : new Integer(slot.indexOrHash));
 
2370
                }
 
2371
                slot = slot.next;
 
2372
            }
 
2373
        }
 
2374
        if (c == a.length)
 
2375
            return a;
 
2376
        Object[] result = new Object[c];
 
2377
        System.arraycopy(a, 0, result, 0, c);
 
2378
        return result;
 
2379
    }
 
2380
 
 
2381
    private synchronized void writeObject(ObjectOutputStream out)
 
2382
        throws IOException
 
2383
    {
 
2384
        out.defaultWriteObject();
 
2385
        int objectsCount = count;
 
2386
        if (objectsCount < 0) {
 
2387
            // "this" was sealed
 
2388
            objectsCount = ~objectsCount;
 
2389
        }
 
2390
        if (objectsCount == 0) {
 
2391
            out.writeInt(0);
 
2392
        } else {
 
2393
            out.writeInt(slots.length);
 
2394
            for (int i = 0; i < slots.length; ++i) {
 
2395
                Slot slot = slots[i];
 
2396
                while (slot != null) {
 
2397
                    out.writeObject(slot);
 
2398
                    slot = slot.next;
 
2399
                    if (--objectsCount == 0)
 
2400
                        return;
 
2401
                }
 
2402
            }
 
2403
        }
 
2404
    }
 
2405
 
 
2406
    private void readObject(ObjectInputStream in)
 
2407
        throws IOException, ClassNotFoundException
 
2408
    {
 
2409
        in.defaultReadObject();
 
2410
        lastAccess = REMOVED;
 
2411
 
 
2412
        int tableSize = in.readInt();
 
2413
        if (tableSize != 0) {
 
2414
            slots = new Slot[tableSize];
 
2415
            int objectsCount = count;
 
2416
            if (objectsCount < 0) {
 
2417
                // "this" was sealed
 
2418
                objectsCount = ~objectsCount;
 
2419
            }
 
2420
            for (int i = 0; i != objectsCount; ++i) {
 
2421
                Slot slot = (Slot)in.readObject();
 
2422
                int slotIndex = getSlotIndex(tableSize, slot.indexOrHash);
 
2423
                addKnownAbsentSlot(slots, slot, slotIndex);
 
2424
            }
 
2425
        }
 
2426
    }
 
2427
 
 
2428
}