001: /*
002: * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.es;
030:
031: /**
032: * JavaScript object
033: */
034: class NativeArray extends Native {
035: static ESId LENGTH = ESId.intern("length");
036: static final int NEW = 1;
037: static final int JOIN = NEW + 1;
038: static final int TO_STRING = JOIN + 1;
039: static final int REVERSE = TO_STRING + 1;
040: static final int SORT = REVERSE + 1;
041:
042: // js1.2
043: static final int CONCAT = SORT + 1;
044: static final int POP = CONCAT + 1;
045: static final int PUSH = POP + 1;
046: static final int SHIFT = PUSH + 1;
047: static final int UNSHIFT = SHIFT + 1;
048: static final int SLICE = UNSHIFT + 1;
049: static final int SPLICE = SLICE + 1;
050:
051: /**
052: * Create a new object based on a prototype
053: */
054: private NativeArray(String name, int n, int len) {
055: super (name, len);
056:
057: this .n = n;
058: }
059:
060: /**
061: * Creates the native Array object
062: */
063: static ESObject create(Global resin) {
064: Native nativeArray = new NativeArray("Array", NEW, 1);
065: ESArray proto = new ESArray();
066: proto.prototype = resin.objProto;
067: NativeWrapper array = new NativeWrapper(resin, nativeArray,
068: proto, ESThunk.ARRAY_THUNK);
069: resin.arrayProto = proto;
070:
071: put(proto, "join", JOIN, 1);
072: put(proto, "toString", TO_STRING, 0);
073: put(proto, "reverse", REVERSE, 0);
074: put(proto, "sort", SORT, 0);
075:
076: // js1.2
077: put(proto, "concat", CONCAT, 0);
078: put(proto, "pop", POP, 0);
079: put(proto, "push", PUSH, 0);
080: put(proto, "shift", SHIFT, 0);
081: put(proto, "unshift", UNSHIFT, 0);
082: put(proto, "slice", SLICE, 2);
083: put(proto, "splice", SPLICE, 0);
084:
085: proto.setClean();
086: array.setClean();
087:
088: return array;
089: }
090:
091: private static void put(ESObject obj, String name, int n, int len) {
092: ESId id = ESId.intern(name);
093:
094: obj.put(id, new NativeArray(name, n, len), DONT_ENUM);
095: }
096:
097: public ESBase call(Call eval, int length) throws Throwable {
098: switch (n) {
099: case NEW:
100: return create(eval, length);
101:
102: case JOIN:
103: if (length == 0)
104: return toString(eval, length);
105: else
106: return join(eval, length);
107:
108: case TO_STRING:
109: return toString(eval, length);
110:
111: case REVERSE:
112: return reverse(eval, length);
113:
114: case SORT:
115: return sort(eval, length);
116:
117: case CONCAT:
118: return concat(eval, length);
119:
120: case POP:
121: return pop(eval, length);
122:
123: case PUSH:
124: return push(eval, length);
125:
126: case SHIFT:
127: return shift(eval, length);
128:
129: case UNSHIFT:
130: return unshift(eval, length);
131:
132: case SLICE:
133: return slice(eval, length);
134:
135: case SPLICE:
136: return splice(eval, length);
137:
138: default:
139: throw new ESException("Unknown object function");
140: }
141: }
142:
143: ESBase create(Call eval, int length) throws Throwable {
144: ESObject obj = Global.getGlobalProto().createArray();
145:
146: if (length == 0)
147: return obj;
148: if (length == 1) {
149: ESBase arg = eval.getArg(0);
150:
151: if (arg instanceof ESNumber)
152: obj.setProperty(LENGTH, ESNumber.create(arg.toInt32()));
153: else
154: obj.setProperty(0, arg);
155:
156: return obj;
157: }
158:
159: for (int i = 0; i < length; i++)
160: obj.setProperty(i, eval.getArg(i));
161:
162: return obj;
163: }
164:
165: static ESBase join(ESObject array, String separator)
166: throws Throwable {
167: if (array.mark != 0) {
168: return ESString.create("...");
169: }
170: array.mark = -1;
171:
172: try {
173: int len = array.getProperty(LENGTH).toInt32();
174: StringBuffer sbuf = new StringBuffer();
175:
176: for (int i = 0; i < len; i++) {
177: if (i != 0)
178: sbuf.append(separator);
179:
180: ESBase value = array.hasProperty(i);
181:
182: if (value != null && value != esNull
183: && value != esUndefined)
184: sbuf.append(value.toString());
185: }
186:
187: return ESString.create(sbuf.toString());
188: } finally {
189: array.mark = 0;
190: }
191: }
192:
193: ESBase join(Call eval, int length) throws Throwable {
194: String separator = length == 0 ? "," : eval.getArg(0)
195: .toString();
196:
197: ESObject array = eval.getArg(-1).toObject();
198:
199: return join(array, separator);
200: }
201:
202: static ESBase toString(ESObject array) throws Throwable {
203: return join(array, ",");
204: }
205:
206: // XXX: different for non-js1.2
207: ESBase toString(Call eval, int length) throws Throwable {
208: ESObject array = eval.getArg(-1).toObject();
209:
210: return toString(array);
211: }
212:
213: ESBase reverse(Call eval, int length) throws Throwable {
214: ESObject array = eval.getArg(-1).toObject();
215:
216: int len = (int) array.getProperty(LENGTH).toInt32();
217: for (int k = 0; k < len / 2; k++) {
218: int firstIndex = k;
219: int secondIndex = len - k - 1;
220:
221: ESBase first = array.hasProperty(firstIndex);
222: ESBase second = array.hasProperty(secondIndex);
223:
224: if (first == null)
225: array.delete(secondIndex);
226: else
227: array.setProperty(secondIndex, first);
228:
229: if (second == null)
230: array.delete(firstIndex);
231: else
232: array.setProperty(firstIndex, second);
233: }
234:
235: return array;
236: }
237:
238: ESBase sort(Call eval, int length) throws Throwable {
239: ESObject array = eval.getArg(-1).toObject();
240:
241: ESBase cmp = length == 0 ? null : eval.getArg(0);
242:
243: int len = (int) array.getProperty(LENGTH).toInt32();
244: ESBase[] values = new ESBase[len];
245:
246: for (int i = 0; i < len; i++)
247: values[i] = array.getProperty("" + i);
248:
249: qsort(values, 0, len, cmp);
250:
251: for (int i = 0; i < len; i++) {
252: if (values[i] == esUndefined)
253: array.delete("" + i);
254: else
255: array.setProperty("" + i, values[i]);
256: }
257:
258: return array;
259: }
260:
261: private void qsort(ESBase[] array, int offset, int length,
262: ESBase cmp) throws Throwable {
263: if (length == 2) {
264: if (compare(cmp, array[offset], array[offset + 1]) > 0) {
265: ESBase temp = array[offset];
266: array[offset] = array[offset + 1];
267: array[offset + 1] = temp;
268: }
269: } else if (length > 2) {
270: int keyIndex = offset + length / 2;
271: ESBase key = array[keyIndex];
272: int keys = 0;
273: int tail = 0;
274: int val;
275:
276: if ((val = compare(cmp, array[offset], key)) > 0) {
277: key = array[offset];
278: array[offset] = array[keyIndex];
279: array[keyIndex] = key;
280: } else if (val == 0)
281: keys++;
282:
283: if ((val = compare(cmp, key, array[offset + length - 1])) > 0) {
284: key = array[offset + length - 1];
285: array[offset + length - 1] = array[keyIndex];
286: array[keyIndex] = key;
287: keys = 0;
288: tail = 1;
289:
290: if ((val = compare(cmp, array[offset], key)) > 0) {
291: key = array[offset];
292: array[offset] = array[keyIndex];
293: array[keyIndex] = key;
294: } else if (val == 0)
295: keys++;
296: } else if (val < 0)
297: tail = 1;
298:
299: int i;
300: if (keyIndex == offset + 1) {
301: i = 2 + tail;
302: keys++;
303: } else
304: i = 1 + tail;
305:
306: for (; i < length; i++) {
307: int index = offset + i - tail;
308:
309: if (array[index] == key) {
310: keys++;
311: continue;
312: }
313:
314: int cmpResult = compare(cmp, key, array[index]);
315: if (cmpResult > 0 && keys != 0) {
316: ESBase temp = array[index];
317: array[index] = array[index - keys];
318: array[index - keys] = temp;
319: } else if (cmpResult < 0) {
320: ESBase temp = array[offset + length - tail - 1];
321: array[offset + length - tail - 1] = array[index];
322: array[index] = temp;
323: tail += 1;
324: } else if (cmpResult == 0)
325: keys++;
326: }
327:
328: if (length - tail - keys > 1)
329: qsort(array, offset, length - tail - keys, cmp);
330: if (tail > 1)
331: qsort(array, offset + length - tail, tail, cmp);
332: }
333: }
334:
335: private int compare(ESBase cmp, ESBase a, ESBase b)
336: throws Throwable {
337: if (a == b)
338: return 0;
339: else if (a == esUndefined)
340: return 1;
341: else if (b == esUndefined)
342: return -1;
343: else if (a == esNull)
344: return 1;
345: else if (b == esNull)
346: return -1;
347: else if (cmp != null) {
348: // Call eval = new Call(ESGlobal.getGlobalProto(), false);
349: Global resin = Global.getGlobalProto();
350: Call eval = resin.getCall();
351:
352: eval.stack[0] = esNull;
353: eval.stack[1] = a;
354: eval.stack[2] = b;
355: eval.top = 1;
356:
357: int result = cmp.call(eval, 2).toInt32();
358:
359: resin.freeCall(eval);
360:
361: return result;
362: } else {
363: String sa = a.toString();
364: String sb = b.toString();
365:
366: return sa.compareTo(sb);
367: }
368: }
369:
370: ESBase concat(Call eval, int length) throws Throwable {
371: ESArray array = Global.getGlobalProto().createArray();
372:
373: int k = 0;
374: for (int i = -1; i < length; i++) {
375: ESBase arg = eval.getArg(i);
376:
377: if (arg == esNull || arg == esUndefined || arg == esEmpty)
378: continue;
379:
380: ESBase arglen = arg.hasProperty(LENGTH);
381:
382: if (arglen == null) {
383: array.setProperty(k++, arg);
384: continue;
385: }
386:
387: int len = (int) arglen.toInt32();
388:
389: if (len < 0) {
390: array.setProperty(k++, arg);
391: continue;
392: }
393:
394: for (int j = 0; j < len; j++) {
395: ESBase obj = arg.hasProperty(j);
396:
397: if (obj != null)
398: array.setProperty(k, obj);
399: k++;
400: }
401: }
402: array.setProperty(LENGTH, ESNumber.create(k));
403:
404: return array;
405: }
406:
407: ESBase pop(Call eval, int length) throws Throwable {
408: ESObject obj = eval.getArg(-1).toObject();
409:
410: ESBase lenObj = obj.hasProperty(LENGTH);
411: int len;
412: if (lenObj == null || (len = lenObj.toInt32()) <= 0)
413: return esUndefined;
414:
415: ESBase value = obj.getProperty(len - 1);
416:
417: obj.setProperty(LENGTH, ESNumber.create(len - 1));
418:
419: return value;
420: }
421:
422: ESBase push(Call eval, int length) throws Throwable {
423: ESObject obj = eval.getArg(-1).toObject();
424:
425: ESBase lenObj = obj.getProperty(LENGTH);
426: int len = lenObj.toInt32();
427: if (len < 0)
428: len = 0;
429:
430: for (int i = 0; i < length; i++)
431: obj.setProperty(len + i, eval.getArg(i));
432:
433: ESNumber newLen = ESNumber.create(len + length);
434: obj.setProperty(LENGTH, newLen);
435:
436: return newLen;
437: }
438:
439: ESBase shift(Call eval, int length) throws Throwable {
440: ESObject obj = eval.getArg(-1).toObject();
441:
442: ESBase lenObj = obj.hasProperty(LENGTH);
443: int len;
444: if (lenObj == null || (len = (int) lenObj.toInt32()) <= 0)
445: return esUndefined;
446:
447: ESBase value = obj.getProperty(0);
448:
449: for (int i = 1; i < len; i++) {
450: ESBase temp = obj.hasProperty(i);
451: if (temp == null)
452: obj.delete(ESString.create(i - 1));
453: else
454: obj.setProperty(i - 1, temp);
455: }
456:
457: obj.setProperty(LENGTH, ESNumber.create(len - 1));
458:
459: return value;
460: }
461:
462: ESBase unshift(Call eval, int length) throws Throwable {
463: ESObject obj = eval.getArg(-1).toObject();
464:
465: ESBase lenObj = obj.getProperty(LENGTH);
466: int len = lenObj.toInt32();
467: if (len < 0)
468: len = 0;
469:
470: if (length == 0)
471: return ESNumber.create(0);
472:
473: for (int i = len - 1; i >= 0; i--) {
474: ESBase value = obj.getProperty(i);
475:
476: if (value == null)
477: obj.delete(ESString.create(length + i));
478: else
479: obj.setProperty(length + i, value);
480: }
481:
482: for (int i = 0; i < length; i++) {
483: ESBase value = eval.getArg(i);
484:
485: if (value == null)
486: obj.delete(ESString.create(i));
487: else
488: obj.setProperty(i, value);
489: }
490:
491: ESNumber numLen = ESNumber.create(len + length);
492: obj.setProperty(LENGTH, numLen);
493:
494: return numLen;
495: }
496:
497: ESBase slice(Call eval, int length) throws Throwable {
498: ESObject obj = eval.getArg(-1).toObject();
499:
500: ESBase lenObj = obj.getProperty(LENGTH);
501: int len = lenObj.toInt32();
502:
503: ESArray array = Global.getGlobalProto().createArray();
504: if (len <= 0)
505: return array;
506:
507: int start = 0;
508: if (length > 0)
509: start = eval.getArg(0).toInt32();
510: if (start < 0)
511: start += len;
512: if (start < 0)
513: start = 0;
514: if (start > len)
515: return array;
516:
517: int end = len;
518: if (length > 1)
519: end = eval.getArg(1).toInt32();
520: if (end < 0)
521: end += len;
522: if (end < 0)
523: return array;
524: if (end > len)
525: end = len;
526:
527: if (start >= end)
528: return array;
529:
530: for (int i = 0; i < end - start; i++) {
531: ESBase value = obj.hasProperty(start + i);
532:
533: if (value != null)
534: array.setProperty(i, value);
535: }
536:
537: array.setProperty(LENGTH, ESNumber.create(end - start));
538:
539: return array;
540: }
541:
542: ESBase splice(Call eval, int length) throws Throwable {
543: if (length < 2)
544: return esUndefined;
545:
546: ESObject obj = eval.getArg(-1).toObject();
547:
548: int index = eval.getArg(0).toInt32();
549: int count = eval.getArg(1).toInt32();
550: boolean single = count == 1;
551:
552: ESBase lenObj = obj.getProperty(LENGTH);
553: int len = lenObj.toInt32();
554:
555: if (index < 0)
556: index += len;
557: if (index < 0)
558: index = 0;
559:
560: if (count < 0)
561: count = 0;
562: if (index + count > len)
563: count = len - index;
564:
565: ESBase value;
566:
567: if (count < 1)
568: value = esUndefined;
569: else {
570: value = Global.getGlobalProto().createArray();
571:
572: for (int i = 0; i < count; i++)
573: value.setProperty(i, obj.getProperty(index + i));
574: }
575:
576: int delta = length - 2 - count;
577: if (delta < 0) {
578: for (int i = 0; i < len - count; i++) {
579: ESBase temp = obj.getProperty(i + index + count);
580: if (temp == null)
581: obj.delete(ESString.create(i + index + count
582: + delta));
583: else
584: obj.setProperty(i + index + count + delta, temp);
585: }
586: } else if (delta > 0) {
587: for (int i = len - count - 1; i >= 0; i--) {
588: ESBase temp = obj.getProperty(i + index + count);
589: if (temp == null)
590: obj.delete(ESString.create(i + index + count
591: + delta));
592: else
593: obj.setProperty(i + index + count + delta, temp);
594: }
595: }
596:
597: for (int i = 0; i < length - 2; i++)
598: obj.setProperty(i + index, eval.getArg(i + 2));
599:
600: obj.setProperty(LENGTH, ESNumber.create(len - count + length
601: - 2));
602:
603: return value;
604: }
605:
606: }
|