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: * Implementation class representing a JavaScript function.
033: */
034: public class ESClosure extends ESObject {
035: static ESString LENGTH = ESId.intern("length");
036: static ESString CALLEE = ESId.intern("callee");
037: static ESString ARGUMENTS = ESId.intern("arguments");
038: static ESString OBJECT = ESId.intern("Object");
039: static ESString PROTOTYPE = ESId.intern("prototype");
040: static ESString CONSTRUCTOR = ESId.intern("constructor");
041:
042: public ESString name;
043: private ESCallable esClass;
044: public int n;
045:
046: ESId[] formals;
047: int nFormals;
048:
049: ESBase proto;
050:
051: ESGlobal global;
052: int stackRequired; // how much argument stack space this function requires
053: int scopeRequired; // how much scope space
054: ESBase[] scope;
055: int scopeLength;
056:
057: boolean hasFields;
058:
059: public ESClosure(ESString name, ESCallable esClass, ESObject proto,
060: int n, ESId[] formals, ESObject global) {
061: this .className = "Function";
062: this .prototype = Global.getGlobalProto().funProto;
063: this .name = name;
064: this .esClass = esClass;
065: this .proto = proto;
066: this .n = n;
067: this .formals = formals;
068: this .nFormals = formals.length;
069:
070: if (global instanceof ESGlobal)
071: this .global = (ESGlobal) global;
072:
073: if (global != null) {
074: this .scopeLength = 1;
075: this .scope = new ESBase[1];
076: this .scope[0] = global;
077: }
078: }
079:
080: protected ESClosure(ESBase[] scope, int scopeLength) {
081: super ("Function", null);
082:
083: hasFields = true;
084: this .scope = scope;
085: this .scopeLength = scopeLength;
086: }
087:
088: /**
089: * Create a new object based on a prototype
090: */
091: protected ESClosure() {
092: }
093:
094: public void closure(Call env) {
095: if (scope == null) {
096: scope = new ESBase[16];
097: }
098:
099: for (; scopeLength < env.scopeLength; scopeLength++)
100: scope[scopeLength] = env.scope[scopeLength];
101:
102: if (scopeLength == 0)
103: scope[scopeLength++] = env.global;
104:
105: global = (ESGlobal) scope[0];
106: }
107:
108: /**
109: * Create a new object based on a prototype
110: */
111: void setScope(ESBase[] scope, int scopeLength) {
112: this .scope = scope;
113: this .scopeLength = scopeLength;
114: }
115:
116: public ESBase hasProperty(ESString id) throws Throwable {
117: if (id.equals(PROTOTYPE)) {
118: if (proto == null) {
119: ESObject obj = Global.getGlobalProto().createObject();
120: proto = obj;
121: obj.put(CONSTRUCTOR, this , DONT_ENUM);
122: }
123:
124: return proto;
125: } else if (id.equals(LENGTH)) {
126: return ESNumber.create(nFormals);
127: } else if (hasFields) {
128: return super .hasProperty(id);
129: } else if (prototype != null)
130: return prototype.hasProperty(id);
131: else
132: return ESBase.esEmpty;
133: }
134:
135: public ESBase getProperty(ESString id) throws Throwable {
136: if (id.equals(PROTOTYPE)) {
137: if (proto == null) {
138: ESObject obj = Global.getGlobalProto().createObject();
139: proto = obj;
140: obj.put(CONSTRUCTOR, this , DONT_ENUM);
141: }
142:
143: return proto;
144: } else if (id.equals(LENGTH)) {
145: return ESNumber.create(nFormals);
146: } else if (hasFields) {
147: return super .getProperty(id);
148: } else
149: return prototype.getProperty(id);
150: }
151:
152: public boolean canPut(ESString id) {
153: if (id.equals(PROTOTYPE)) {
154: return true;
155: } else if (id.equals(LENGTH)) {
156: return false;
157: } else if (hasFields) {
158: return super .canPut(id);
159: } else {
160: return true;
161: }
162: }
163:
164: public void setProperty(ESString id, ESBase value) throws Throwable {
165: if (id.equals(PROTOTYPE)) {
166: proto = value;
167: } else if (id.equals(LENGTH)) {
168: } else if (hasFields) {
169: super .setProperty(id, value);
170: } else {
171: init(className, prototype);
172: hasFields = true;
173: super .setProperty(id, value);
174: }
175: }
176:
177: public void put(ESString id, ESBase value, int flags) {
178: if (id.equals(PROTOTYPE)) {
179: proto = value;
180: } else if (id.equals(LENGTH)) {
181: } else if (hasFields) {
182: super .put(id, value, flags);
183: } else {
184: init(className, prototype);
185: hasFields = true;
186: super .put(id, value, flags);
187: }
188: }
189:
190: public ESBase delete(ESString id) throws Throwable {
191: if (id.equals(PROTOTYPE)) {
192: proto = ESBase.esEmpty;
193: return ESBoolean.TRUE;
194: } else if (id.equals(LENGTH)) {
195: return ESBoolean.FALSE;
196: } else if (hasFields) {
197: return super .delete(id);
198: } else
199: return ESBoolean.TRUE;
200: }
201:
202: public ESString toStr() {
203: return ESString.create(decompile());
204: }
205:
206: String decompile() {
207: StringBuffer sbuf = new StringBuffer();
208:
209: sbuf.append("function ");
210: if (name != null && !name.toString().startsWith("$lambda"))
211: sbuf.append(name);
212: sbuf.append("(");
213: for (int i = 0; formals != null && i < nFormals; i++) {
214: if (i != 0)
215: sbuf.append(", ");
216: sbuf.append(formals[i]);
217: }
218:
219: sbuf.append(") ");
220:
221: sbuf.append("{ ");
222: sbuf.append("[compiled code]");
223: sbuf.append(" }");
224:
225: return sbuf.toString();
226: }
227:
228: protected ESBase dispatch() throws ESException {
229: throw new ESException("dispatch not specialized");
230: }
231:
232: public ESBase call(Call call, int length) throws Throwable {
233: if (global != null)
234: call.global = global;
235:
236: if (esClass != null)
237: return esClass.call(n, call, length);
238: else
239: return ESBase.esUndefined;
240: }
241:
242: public ESBase construct(Call eval, int length) throws Throwable {
243: Global resin = Global.getGlobalProto();
244: ESObject obj = Global.getGlobalProto().createObject();
245: ESBase proto = this .proto;
246:
247: if (!(proto instanceof ESObject))
248: proto = resin.object.getProperty(PROTOTYPE);
249:
250: if (proto instanceof ESObject)
251: obj.prototype = proto;
252:
253: eval.setThis(obj);
254:
255: ESBase value = call(eval, length);
256:
257: return value instanceof ESObject ? (ESObject) value : obj;
258: }
259:
260: public ESBase typeof() throws ESException {
261: return ESString.create("function");
262: }
263:
264: protected void copy(Object newObj) {
265: ESClosure closure = (ESClosure) newObj;
266:
267: super .copy(newObj);
268:
269: closure.name = name;
270: closure.esClass = esClass;
271: closure.n = n;
272: closure.formals = formals;
273: closure.nFormals = nFormals;
274:
275: closure.stackRequired = stackRequired;
276: closure.scopeRequired = scopeRequired;
277: closure.scopeLength = scopeLength;
278: closure.scope = new ESBase[scopeLength];
279: closure.hasFields = hasFields;
280:
281: for (int i = 0; i < scopeLength; i++) {
282: if (scope[i] != null) {
283: closure.scope[i] = (ESBase) scope[i];
284: }
285: }
286:
287: closure.proto = proto; // XXX: should be copied?
288: }
289:
290: public ESObject dup() {
291: return new ESClosure();
292: }
293:
294: ESObject resinCopy() {
295: ESObject obj = dup();
296:
297: copy(obj);
298:
299: return obj;
300: }
301:
302: ESObject getClassPrototype() throws Throwable {
303: ESBase proto = hasProperty(PROTOTYPE);
304:
305: return (ESObject) proto;
306: }
307:
308: void setClassPrototype(ESObject proto) throws ESException {
309: this.proto = proto;
310: proto.put(CONSTRUCTOR, this, DONT_ENUM);
311: }
312: }
|