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.es.parser.Parser;
032: import com.caucho.vfs.ReadStream;
033: import com.caucho.vfs.Vfs;
034:
035: import java.io.IOException;
036: import java.util.ArrayList;
037:
038: /**
039: * JavaScript object
040: */
041: class NativeFunction extends Native {
042: static final int NEW = 1;
043: static final int TO_STRING = NEW + 1;
044: static final int CALL = TO_STRING + 1;
045: static final int APPLY = CALL + 1;
046:
047: static ESId LENGTH = ESId.intern("length");
048:
049: /**
050: * Create a new object based on a prototype
051: */
052: private NativeFunction(String name, int n, int len) {
053: super (name, len);
054:
055: this .n = n;
056: }
057:
058: /**
059: * Creates the native Function object
060: *
061: * XXX: incomplete
062: */
063: static NativeWrapper create(Global resin) {
064: ESBase[] scope = new ESBase[] { resin.getGlobalProto() };
065: ESClosure funProto = new ESClosure(scope, 1);
066: funProto.name = ESId.intern("Function");
067:
068: Native natFunction = new NativeFunction("Function", NEW, 1);
069: NativeWrapper function = new NativeWrapper(resin, natFunction,
070: funProto, ESThunk.FUN_THUNK);
071: resin.funProto = funProto;
072:
073: put(funProto, "toString", TO_STRING, 0);
074: put(funProto, "call", CALL, 1);
075: put(funProto, "apply", APPLY, 2);
076: funProto.prototype = resin.objProto;
077:
078: funProto.setClean();
079: function.setClean();
080:
081: return function;
082: }
083:
084: private static void put(ESObject proto, String name, int n, int len) {
085: ESId id = ESId.intern(name);
086:
087: proto.put(id, new NativeFunction(name, n, len), DONT_ENUM);
088: }
089:
090: public ESBase call(Call eval, int length) throws Throwable {
091: switch (n) {
092: case NEW:
093: return createAnonymous(eval, length);
094:
095: // Object prototype stuff
096: case TO_STRING:
097: // XXX: Is this correct? Test.
098: if (eval.getThis() instanceof ESClosure) {
099: ESClosure closure = (ESClosure) eval.getThis();
100:
101: return ESString.create(closure.decompile());
102: } else if (eval.getThis() instanceof NativeWrapper) {
103: NativeWrapper wrapper = (NativeWrapper) eval.getThis();
104:
105: return wrapper.fun.toStr();
106: } else
107: throw new ESException("to string bound to function: "
108: + eval.getThis().getClass());
109:
110: case CALL:
111: int oldTop = eval.top;
112: ESBase fun = eval.getArg(-1);
113: ESBase callThis = null;
114:
115: try {
116: if (length > 0) {
117: callThis = eval.getArg(0);
118: } else
119: callThis = esNull;
120:
121: if (callThis == esNull || callThis == esUndefined
122: || callThis == esEmpty)
123: eval.setArg(0, eval.getGlobal());
124: else
125: eval.setArg(0, callThis.toObject());
126: eval.top++;
127:
128: return fun.call(eval, length > 0 ? length - 1 : 0);
129: } finally {
130: eval.top = oldTop;
131: }
132:
133: case APPLY:
134: return apply(eval, length);
135:
136: default:
137: throw new ESException("Unknown object function");
138: }
139: }
140:
141: /**
142: * Create a new parser instance.
143: */
144: private ESClosure parseFunction(Call eval, int length)
145: throws Throwable {
146: StringBuffer sbuf = new StringBuffer();
147:
148: sbuf.append("function anonymous(");
149: ArrayList argList = new ArrayList();
150: for (int i = 0; i < length - 1; i++) {
151: if (i != 0)
152: sbuf.append(",");
153: String str = eval.getArg(i).toString();
154: int j = 0;
155: int p = 0;
156:
157: while ((p = str.indexOf(',', j)) >= 0
158: || (p = str.indexOf(' ', j)) >= 0) {
159: if (j < p)
160: argList.add(ESId.intern(str.substring(j, p)));
161: j = p + 1;
162: }
163: if (j < str.length())
164: argList.add(ESId.intern(str.substring(j)));
165:
166: sbuf.append(str);
167: }
168: ESId[] args = new ESId[argList.size()];
169: argList.toArray(args);
170: sbuf.append("){");
171: if (length > 0)
172: sbuf.append(eval.getArg(length - 1).toString());
173: sbuf.append("}\n");
174: sbuf.append("return anonymous();");
175:
176: Global resin = Global.getGlobalProto();
177: Script script = null;
178: try {
179: Parser parser = new Parser();
180: ReadStream is = Vfs.openString(sbuf.toString());
181: script = parser.parse(is, "anonymous", 1);
182: is.close();
183: } catch (IOException e) {
184: e.printStackTrace();
185: }
186:
187: ESCallable jsClass = script.initClass(resin, eval.getGlobal());
188:
189: // It's known that the function will be #2.
190: ESClosure fun = new ESClosure(ESId.intern("anonymous"),
191: jsClass, null, 2, args, eval.getGlobal());
192:
193: return fun;
194: }
195:
196: private ESBase createAnonymous(Call eval, int length)
197: throws Throwable {
198: return parseFunction(eval, length);
199: }
200:
201: private ESBase apply(Call eval, int length) throws Throwable {
202: Global resin = Global.getGlobalProto();
203: Call call = eval.getCall();
204:
205: call.top = 1;
206: call.global = eval.global;
207: call.caller = eval;
208:
209: ESBase fun = eval.getArg(-1);
210: ESBase callThis = null;
211:
212: if (length > 0) {
213: callThis = eval.getArg(0);
214: } else
215: callThis = esNull;
216:
217: if (callThis == esNull || callThis == esUndefined
218: || callThis == esEmpty)
219: call.setArg(-1, eval.getGlobal());
220: else
221: call.setArg(-1, callThis.toObject());
222:
223: int j = 0;
224: for (int i = 1; i < length; i++) {
225: ESBase arg = eval.getArg(i);
226:
227: if (arg == esNull || arg == esUndefined || arg == esEmpty)
228: continue;
229:
230: ESBase arglen = arg.hasProperty(LENGTH);
231:
232: if (arglen == null)
233: call.setArg(j++, arg);
234: else {
235: int len = arglen.toInt32();
236:
237: if (j + len > call.stack.length - 2)
238: throw new ESException("stack overflow");
239:
240: for (int k = 0; k < len; k++)
241: call.setArg(j++, arg
242: .getProperty(ESString.create(k)));
243:
244: if (len < 0)
245: call.setArg(j++, arg);
246: }
247: }
248:
249: ESBase value = fun.call(call, j);
250:
251: resin.freeCall(call);
252:
253: return value;
254: }
255: }
|