KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > es > ESObject


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.es;
30
31 import com.caucho.util.CharBuffer;
32 import com.caucho.util.IntMap;
33
34 import java.util.HashMap JavaDoc;
35 import java.util.Iterator JavaDoc;
36
37 /**
38  * Implementation class for a JavaScript Object.
39  */

40 public class ESObject extends ESBase {
41   static ESId TO_STRING = ESId.intern("toString");
42   static ESId VALUE_OF = ESId.intern("valueOf");
43   static ESId CALL = ESId.intern("call");
44   static ESId CONSTRUCT = ESId.intern("construct");
45   static int DIRTY = 0;
46   static int CLEAN = DIRTY + 1;
47   static int COW = CLEAN + 1;
48
49   int copyState = DIRTY;
50
51   ESString []propNames;
52   ESBase []propValues;
53   ESBase []propWatch;
54   int []propFlags;
55   int size;
56   int fill;
57   int mask;
58
59   int mark; // for printing
60
protected boolean snapPrototype;
61
62   protected ESObject()
63   {
64   }
65
66   /**
67    * Simple constructor for parentless objects.
68    */

69   public ESObject(String JavaDoc className, ESBase proto)
70   {
71     init(className, proto, 16);
72   }
73
74   protected ESObject(String JavaDoc className, ESBase proto, int hashSize)
75   {
76     init(className, proto, hashSize < 16 ? 16 : hashSize);
77   }
78
79   private void init(String JavaDoc className, ESBase proto, int hashSize)
80   {
81     if (proto == null) {
82       Global resin = Global.getGlobalProto();
83       proto = resin == null ? null : resin.objProto;
84     }
85     if (className == null && proto != null)
86       className = proto.className;
87     prototype = proto;
88
89     propNames = new ESString[hashSize];
90     propValues = new ESBase[hashSize];
91     propFlags = new int[hashSize];
92     mask = propNames.length - 1;
93     size = 0;
94     fill = 0;
95     copyState = DIRTY;
96
97     this.className = className == null ? "Object" : className;
98   }
99
100   void init(String JavaDoc className, ESBase proto)
101   {
102     init(className, proto, 16);
103   }
104
105   void setClean()
106   {
107     copyState = CLEAN;
108   }
109
110   /**
111    * Expands the property table
112    */

113   private void resize(int newSize)
114   {
115     ESString []newNames = new ESString[newSize];
116     ESBase []newValues = new ESBase[newSize];
117     int []newFlags = new int[newSize];
118     ESBase []newWatch = null;
119     if (propWatch != null)
120       newWatch = new ESBase[newSize];
121
122     mask = newNames.length - 1;
123
124     for (int i = 0; i < propNames.length; i++) {
125       if (propValues[i] == null && (propFlags[i] & WATCH) == 0)
126     continue;
127
128       int hash = propNames[i].hashCode() & mask;
129
130       while (true) {
131     if (newNames[hash] == null) {
132       newNames[hash] = propNames[i];
133       newValues[hash] = propValues[i];
134       newFlags[hash] = propFlags[i];
135       if (newWatch != null)
136         newWatch[hash] = propWatch[i];
137       break;
138     }
139     hash = (hash + 1) & mask;
140       }
141     }
142
143     propNames = newNames;
144     propValues = newValues;
145     propFlags = newFlags;
146     propWatch = newWatch;
147     fill = size;
148   }
149
150   private void refill()
151   {
152     for (int i = 0; i < propNames.length; i++) {
153       if (propValues[i] == null && (propFlags[i] & WATCH) == 0) {
154     propNames[i] = null;
155     continue;
156       }
157
158       int hash = propNames[i].hashCode() & mask;
159
160       while (true) {
161     if (propValues[hash] == null && (propFlags[hash] & WATCH) == 0) {
162       propNames[hash] = propNames[i];
163       propValues[hash] = propValues[i];
164       propFlags[hash] = propFlags[i];
165       propNames[i] = null;
166       propValues[i] = null;
167       propFlags[i] = 0;
168       break;
169     }
170     hash = (hash + 1) & mask;
171       }
172     }
173
174     fill = size;
175   }
176
177   /**
178    * Gets a property value.
179    */

180   public ESBase getProperty(ESString name) throws Throwable JavaDoc
181   {
182     int hash = name.hashCode() & mask;
183
184     while (true) {
185       ESString propName = propNames[hash];
186
187       if (propName == name || name.equals(propName)) {
188     ESBase value = propValues[hash];
189     return value == null ? prototype.getProperty(name) : value;
190       }
191       else if (propName == null) {
192     ESBase value = prototype.getProperty(name);
193     if (snapPrototype)
194       setProperty(name, value);
195     return value;
196       }
197
198       hash = (hash + 1) & mask;
199     }
200   }
201
202   protected boolean canPut(ESString name)
203   {
204     int hash = name.hashCode() & mask;
205
206     while (true) {
207       ESString propName = propNames[hash];
208
209       if (name.equals(propName) && propValues[hash] != null)
210     return (propFlags[hash] & READ_ONLY) == 0;
211       else if (propName == null) {
212     if (prototype instanceof ESObject)
213       return ((ESObject) prototype).canPut(name);
214     else
215       return true;
216       }
217
218       hash = (hash + 1) & mask;
219     }
220   }
221
222   /*
223   public ESBase callWatch(ESString name, int hash, ESBase value)
224   throws Exception
225   {
226     Global resin = Global.getGlobalProto();
227     Call call = resin.getCall();
228     call.top = 1;
229
230     if (propWatch[hash] instanceof ESClosure)
231       call.setArg(-1, ((ESClosure) propWatch[hash]).scope[0]);
232     else
233       call.setArg(-1, null);
234     call.setArg(0, name);
235     call.setArg(1, propValues[hash]);
236     call.setArg(2, value);
237     value = propWatch[hash].call(call, 3);
238     resin.freeCall(call);
239
240     return value;
241   }
242   */

243
244   /**
245    * Puts a new value in the property table with the appropriate flags
246    */

247   public void setProperty(ESString name, ESBase value) throws Throwable JavaDoc
248   {
249     if (copyState != DIRTY) {
250       if (copyState == COW)
251     copyAll();
252       copyState = DIRTY;
253     }
254
255     if (value == esEmpty)
256       value = esUndefined;
257
258     int hash = name.hashCode() & mask;
259
260     while (true) {
261       ESString propName = propNames[hash];
262
263       if (propValues[hash] == null) {
264     if (! prototype.canPut(name))
265       return;
266
267     if (propName == null)
268       fill++;
269
270     propNames[hash] = name;
271         /*
272     if ((propFlags[hash] & WATCH) != 0)
273       value = callWatch(name, hash, value);
274         */

275     propValues[hash] = value;
276     propFlags[hash] = 0;
277
278     size++;
279
280     if (propNames.length <= 4 * size) {
281       resize(4 * propNames.length);
282     }
283     else if (propNames.length <= 2 * fill)
284       refill();
285
286     return;
287       }
288       else if (propName != name && ! propName.equals(name)) {
289     hash = (hash + 1) & mask;
290     continue;
291       }
292       else if ((propFlags[hash] & READ_ONLY) != 0)
293     return;
294       else {
295         /*
296     if ((propFlags[hash] & WATCH) != 0)
297       value = callWatch(name, hash, value);
298         */

299     propValues[hash] = value;
300     return;
301       }
302     }
303   }
304
305   public void put(ESString name, ESBase value, int flags)
306   {
307     int hash = name.hashCode() & mask;
308
309     while (true) {
310       ESString propName = propNames[hash];
311
312       if (propName == null ||
313       propValues[hash] == null || propName.equals(name)) {
314     if (propName == null)
315       fill++;
316     if (propValues[hash] == null)
317       size++;
318
319     propNames[hash] = name;
320     propValues[hash] = value;
321     propFlags[hash] = flags;
322
323     if (propNames.length <= 4 * size) {
324       resize(4 * propNames.length);
325     }
326     else if (propNames.length <= 2 * fill)
327       refill();
328
329     return;
330       }
331
332       hash = (hash + 1) & mask;
333     }
334   }
335
336   public void put(String JavaDoc name, ESBase value, int flags)
337   {
338     ESId id = ESId.intern(name);
339
340     put(id, value, flags);
341   }
342
343   /**
344    * Deletes the entry. Returns true if successful.
345    */

346   public ESBase delete(ESString name) throws Throwable JavaDoc
347   {
348     if (copyState != DIRTY) {
349       if (copyState == COW)
350     copyAll();
351       copyState = DIRTY;
352     }
353
354     int hash = name.hashCode() & mask;
355
356     while (true) {
357       ESString hashName = propNames[hash];
358
359       if (hashName == null)
360     return ESBoolean.FALSE;
361       else if (propValues[hash] != null && hashName.equals(name)) {
362     if ((propFlags[hash] & DONT_DELETE) != 0)
363       return ESBoolean.FALSE;
364     else {
365       propValues[hash] = null;
366       size--;
367       return ESBoolean.TRUE;
368     }
369       }
370
371       hash = (hash + 1) & mask;
372     }
373   }
374
375   public void watch(ESString name, ESBase fun)
376   {
377     if (copyState != DIRTY) {
378       if (copyState == COW)
379     copyAll();
380       copyState = DIRTY;
381     }
382
383     int hash = name.hashCode() & mask;
384
385     while (true) {
386       ESString propName = propNames[hash];
387
388       if (propValues[hash] == null) {
389     if (! prototype.canPut(name))
390       return;
391
392     if (propName == null)
393       fill++;
394
395     propNames[hash] = name;
396     propValues[hash] = esEmpty;
397     propFlags[hash] = WATCH;
398     if (propWatch == null)
399       propWatch = new ESBase[propFlags.length];
400     propWatch[hash] = fun;
401
402     size++;
403
404     if (propNames.length <= 4 * size)
405       resize(4 * propNames.length);
406     else if (propNames.length <= 2 * fill)
407       refill();
408
409     return;
410       }
411       else if (propName != name && ! propName.equals(name)) {
412     hash = (hash + 1) & mask;
413     continue;
414       }
415       else if ((propFlags[hash] & READ_ONLY) != 0)
416     return;
417       else {
418     propFlags[hash] |= WATCH;
419     if (propWatch == null)
420       propWatch = new ESBase[propFlags.length];
421
422     propWatch[hash] = fun;
423     return;
424       }
425     }
426   }
427
428   public void unwatch(ESString name)
429   {
430     if (copyState != DIRTY) {
431       if (copyState == COW)
432     copyAll();
433       copyState = DIRTY;
434     }
435
436     int hash = name.hashCode() & mask;
437
438     while (true) {
439       ESString propName = propNames[hash];
440
441       if (propName == null)
442     return;
443       else if (propName.equals(name)) {
444     propFlags[hash] &= ~WATCH;
445
446     return;
447       }
448     }
449   }
450
451   /**
452    * Sets the named property
453    */

454   public void put(int i, ESBase value, int flags)
455   {
456     put(ESString.create(i), value, flags);
457   }
458
459   public Iterator JavaDoc keys() throws ESException
460   {
461     return new PropertyEnumeration(this);
462   }
463
464   public ESBase typeof() throws ESException
465   {
466     return ESString.create("object");
467   }
468
469   /**
470    * XXX: not right
471    */

472   public ESBase toPrimitive(int hint) throws Throwable JavaDoc
473   {
474     Global resin = Global.getGlobalProto();
475     Call eval = resin.getCall();
476     eval.global = resin.getGlobal();
477
478     try {
479       ESBase fun = hasProperty(hint == STRING ? TO_STRING : VALUE_OF);
480
481       if (fun instanceof ESClosure || fun instanceof Native) {
482     eval.stack[0] = this;
483     eval.top = 1;
484     ESBase value = fun.call(eval, 0);
485
486     if (value instanceof ESBase && ! (value instanceof ESObject))
487       return value;
488       }
489
490       fun = hasProperty(hint == STRING ? VALUE_OF : TO_STRING);
491
492       if (fun instanceof ESClosure || fun instanceof Native) {
493     eval.stack[0] = this;
494     eval.top = 1;
495     ESBase value = fun.call(eval, 0);
496
497     if (value instanceof ESBase && ! (value instanceof ESObject))
498       return value;
499       }
500
501       throw new ESException("cannot convert object to primitive type");
502     } finally {
503       resin.freeCall(eval);
504     }
505   }
506
507   public ESObject toObject() { return this; }
508
509   public Object JavaDoc toJavaObject() throws ESException
510   {
511     return this;
512   }
513
514   /**
515    * Returns a string rep of the object
516    */

517   public double toNum() throws Throwable JavaDoc
518   {
519     ESBase value = toPrimitive(NUMBER);
520
521     if (value instanceof ESObject)
522       throw new ESException("toPrimitive must return primitive");
523
524     return value.toNum();
525   }
526
527   /**
528    * Returns a string rep of the object
529    */

530   public ESString toStr() throws Throwable JavaDoc
531   {
532     ESBase prim = toPrimitive(STRING);
533
534     if (prim instanceof ESObject)
535       throw new ESException("toPrimitive must return primitive");
536
537     return prim.toStr();
538   }
539
540   public ESString toSource(IntMap map, boolean isLoopPass) throws Throwable JavaDoc
541   {
542     CharBuffer cb = new CharBuffer();
543     Global resin = Global.getGlobalProto();
544
545     int mark = map.get(this);
546     
547     if (mark > 0 && isLoopPass)
548       return null;
549     else if (mark > 0) {
550       cb.append("#" + mark + "=");
551       map.put(this, -mark);
552     } else if (mark == 0 && isLoopPass) {
553       map.put(this, resin.addMark());
554       return null;
555     } else if (mark < 0 && ! isLoopPass) {
556       return ESString.create("#" + -mark + "#");
557     }
558
559     cb.append("{");
560
561     if (isLoopPass)
562       map.put(this, 0);
563
564     Iterator JavaDoc e = keys();
565
566     boolean isFirst = true;
567     while (e.hasNext()) {
568       if (! isFirst)
569     cb.append(", ");
570       isFirst = false;
571
572       ESString key = (ESString) e.next();
573
574       cb.append(key);
575       cb.append(":");
576       ESBase value = getProperty(key);
577       if (isLoopPass)
578     value.toSource(map, isLoopPass);
579       else
580     cb.append(value.toSource(map, isLoopPass));
581     }
582
583     cb.append("}");
584
585     return new ESString(cb.toString());
586   }
587
588   public boolean toBoolean() { return true; }
589   
590   ESObject dup() { return new ESObject(); }
591   
592   public Object JavaDoc copy(HashMap JavaDoc refs)
593   {
594     Object JavaDoc ref = refs.get(this);
595     if (ref != null)
596       return ref;
597
598     ESObject copy = dup();
599     refs.put(this, copy);
600
601     copy(refs, copy);
602
603     return copy;
604   }
605
606   private void copyAll()
607   {
608     copyState = DIRTY;
609
610     int len = propValues.length;
611
612     ESString []newPropNames = new ESString[len];
613     int []newPropFlags = new int[len];
614     ESBase []newPropValues = new ESBase[len];
615
616     System.arraycopy(propNames, 0, newPropNames, 0, len);
617     System.arraycopy(propFlags, 0, newPropFlags, 0, len);
618     System.arraycopy(propValues, 0, newPropValues, 0, len);
619
620     propNames = newPropNames;
621     propFlags = newPropFlags;
622     propValues = newPropValues;
623
624     if (propWatch != null) {
625       ESBase []newPropWatch = new ESBase[len];
626       System.arraycopy(propWatch, 0, newPropWatch, 0, len);
627       propWatch = newPropWatch;
628     }
629   }
630
631   ESObject resinCopy()
632   {
633     ESObject obj = dup();
634
635     copy(obj);
636
637     return obj;
638   }
639
640   protected void copy(Object JavaDoc newObj)
641   {
642     ESObject obj = (ESObject) newObj;
643
644     obj.prototype = prototype;
645     obj.className = className;
646
647     obj.propNames = propNames;
648     obj.propValues = propValues;
649     obj.propFlags = propFlags;
650     obj.propWatch = propWatch;
651     obj.size = size;
652     obj.fill = fill;
653     obj.mask = mask;
654     obj.copyState = copyState;
655
656     if (obj.copyState == DIRTY) {
657       throw new RuntimeException JavaDoc();
658     }
659     else if (copyState == CLEAN) {
660       copyState = COW;
661       obj.copyState = COW;
662     }
663   }
664
665   protected void copy(HashMap JavaDoc refs, Object JavaDoc newObj)
666   {
667     ESObject obj = (ESObject) newObj;
668
669     obj.prototype = (ESBase) prototype.copy(refs);
670     obj.className = className;
671
672     obj.propNames = propNames;
673     obj.propValues = propValues;
674     obj.propFlags = propFlags;
675     obj.propWatch = propWatch;
676     obj.size = size;
677     obj.fill = fill;
678     obj.mask = mask;
679     obj.copyState = copyState;
680
681     if (obj.copyState == DIRTY) {
682       obj.copyAll();
683     }
684     else if (copyState == CLEAN) {
685       copyState = COW;
686       obj.copyState = COW;
687     }
688   }
689
690   ESObject shallowCopy()
691   {
692     ESObject obj = dup();
693
694     shallowCopy(obj);
695
696     return obj;
697   }
698
699   protected void shallowCopy(Object JavaDoc newObj)
700   {
701     ESObject obj = (ESObject) newObj;
702
703     obj.prototype = prototype;
704     obj.className = className;
705
706     int len = propValues.length;
707
708     if (propWatch != null) {
709       obj.propWatch = new ESBase[len];
710       System.arraycopy(propWatch, 0, obj.propWatch, 0, len);
711     }
712
713     obj.propNames = new ESString[len];
714     obj.propFlags = new int[len];
715     obj.propValues = new ESBase[len];
716
717     ESString []newNames = obj.propNames;
718     ESString []oldNames = propNames;
719     ESBase []newValues = obj.propValues;
720     ESBase []oldValues = propValues;
721     int []newFlags = obj.propFlags;
722     int []oldFlags = propFlags;
723     for (int i = 0; i < len; i++) {
724       newNames[i] = oldNames[i];
725       newValues[i] = oldValues[i];
726       newFlags[i] = oldFlags[i];
727     }
728
729     obj.size = size;
730     obj.mask = mask;
731     obj.fill = fill;
732     obj.copyState = DIRTY;
733   }
734
735   public boolean ecmaEquals(ESBase b) throws Throwable JavaDoc
736   {
737     if (b instanceof ESObject || b instanceof ESThunk)
738       return this == b;
739     else
740       return toPrimitive(NONE).ecmaEquals(b);
741   }
742
743   public ESBase call(Call call, int length) throws Throwable JavaDoc
744   {
745     ESBase callFun = hasProperty(CALL);
746
747     if (callFun != null) {
748       call.setThis(this);
749       return callFun.call(call, length);
750     }
751
752     throw new ESNullException(toStr() + " is not a function");
753   }
754
755   public ESBase construct(Call call, int length) throws Throwable JavaDoc
756   {
757     ESBase callFun = hasProperty(CONSTRUCT);
758
759     if (callFun != null) {
760       call.setThis(this);
761       return callFun.construct(call, length);
762     }
763
764     throw new ESNullException(toStr() + " is not a constructor");
765   }
766 }
767
Popular Tags