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: import com.caucho.util.IntMap;
032:
033: /**
034: * JavaScript object
035: */
036: class NativeObject extends Native {
037: static final int TO_OBJECT = 2;
038: static final int TO_STRING = TO_OBJECT + 1;
039: static final int VALUE_OF = TO_STRING + 1;
040: static final int TO_SOURCE = VALUE_OF + 1;
041: static final int WATCH = TO_SOURCE + 1;
042: static final int UNWATCH = WATCH + 1;
043:
044: /**
045: * Create a new object based on a prototype
046: */
047: private NativeObject(String name, int n, int len) {
048: super (name, len);
049:
050: this .n = n;
051: }
052:
053: /**
054: * Creates the native Object object
055: */
056: static ESObject create(Global resin) {
057: Native nativeObj = new NativeObject("Object", TO_OBJECT, 1);
058: resin.objProto = new ESObject("Object", esBase);
059:
060: NativeWrapper obj = new NativeWrapper(resin, nativeObj,
061: resin.objProto, ESThunk.OBJ_THUNK);
062:
063: put(resin.objProto, "toString", TO_STRING, 0, DONT_ENUM);
064: put(resin.objProto, "valueOf", VALUE_OF, 0, DONT_ENUM);
065:
066: put(resin.objProto, "toSource", TO_SOURCE, 0, DONT_ENUM);
067: put(resin.objProto, "watch", WATCH, 0, DONT_ENUM);
068: put(resin.objProto, "unwatch", UNWATCH, 0, DONT_ENUM);
069:
070: resin.objProto.setClean();
071: obj.setClean();
072:
073: return obj;
074: }
075:
076: private static void put(ESObject obj, String name, int n, int len,
077: int flags) {
078: ESId id = ESId.intern(name);
079: NativeObject fun = new NativeObject(name, n, len);
080:
081: try {
082: obj.put(id, fun, flags);
083: } catch (Exception e) {
084: throw new RuntimeException();
085: }
086: }
087:
088: public ESBase call(Call eval, int length) throws Throwable {
089: switch (n) {
090: // Object prototype stuff
091: case TO_STRING:
092: // XXX: Is this correct? Test.
093: ESBase arg = eval.getArg(-1);
094:
095: if (arg instanceof ESObject)
096: return toString((ESObject) arg);
097: else
098: return toString(arg.toObject());
099:
100: case VALUE_OF:
101: arg = eval.getArg(-1);
102: if (arg instanceof ESWrapper) {
103: ESWrapper obj = (ESWrapper) arg;
104:
105: if (obj.value instanceof ESBase)
106: return (ESBase) obj.value;
107: else
108: return obj.toStr();
109: }
110: return arg;
111:
112: case TO_OBJECT:
113: if (length <= 0 || (arg = eval.getArg(0)) == ESBase.esNull
114: || arg == ESBase.esUndefined
115: || arg == ESBase.esEmpty)
116: return Global.getGlobalProto().createObject();
117:
118: else if (length > 1)
119: return createObjectLiteral(eval, length);
120:
121: else
122: return arg.toObject();
123:
124: case TO_SOURCE:
125: arg = eval.getThis();
126: Global.getGlobalProto().clearMark();
127: IntMap map = new IntMap();
128:
129: arg.toSource(map, true);
130: return arg.toSource(map, false);
131:
132: case WATCH:
133: if (length < 2)
134: throw new ESException("watch expects two arguments");
135:
136: ESBase obj = eval.getThis();
137:
138: ESString key = eval.getArg(0).toStr();
139: ESBase fun = eval.getArg(1);
140: if (!(fun instanceof ESClosure) && !(fun instanceof Native))
141: throw new ESException("watch requires function");
142:
143: ((ESObject) obj).watch(key, fun);
144:
145: return esUndefined;
146:
147: case UNWATCH:
148: if (length < 1)
149: throw new ESException("unwatch expects one argument");
150:
151: obj = eval.getThis();
152:
153: key = eval.getArg(0).toStr();
154:
155: ((ESObject) obj).unwatch(key);
156:
157: return esUndefined;
158:
159: default:
160: throw new RuntimeException("Unknown object function");
161: }
162: }
163:
164: public ESBase construct(Call eval, int length) throws Throwable {
165: if (n != TO_OBJECT)
166: return super .construct(eval, length);
167:
168: if (length == 0 || eval.getArg(0) == esNull
169: || eval.getArg(0) == esUndefined
170: || eval.getArg(0) == esEmpty) {
171: return Global.getGlobalProto().createObject();
172: }
173: if (length > 1) {
174: return createObjectLiteral(eval, length);
175: }
176:
177: return eval.getArg(0).toObject();
178: }
179:
180: private ESBase createObjectLiteral(Call call, int length)
181: throws Throwable {
182: ESObject obj = Global.getGlobalProto().createObject();
183:
184: for (int i = 0; i + 1 < length; i += 2) {
185: ESString key = call.getArg(i, length).toStr();
186: ESBase value = call.getArg(i + 1, length);
187:
188: obj.setProperty(key, value);
189: }
190:
191: return obj;
192: }
193:
194: static public ESBase toString(ESObject obj) throws ESException {
195: return ESString.create("[object " + obj.getClassName() + "]");
196: }
197: }
|