/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.es; /** * Implementation class representing a JavaScript function. */ public class ESClosure extends ESObject { static ESString LENGTH = ESId.intern("length"); static ESString CALLEE = ESId.intern("callee"); static ESString ARGUMENTS = ESId.intern("arguments"); static ESString OBJECT = ESId.intern("Object"); static ESString PROTOTYPE = ESId.intern("prototype"); static ESString CONSTRUCTOR = ESId.intern("constructor"); public ESString name; private ESCallable esClass; public int n; ESId []formals; int nFormals; ESBase proto; ESGlobal global; int stackRequired; // how much argument stack space this function requires int scopeRequired; // how much scope space ESBase []scope; int scopeLength; boolean hasFields; public ESClosure(ESString name, ESCallable esClass, ESObject proto, int n, ESId []formals, ESObject global) { this.className = "Function"; this.prototype = Global.getGlobalProto().funProto; this.name = name; this.esClass = esClass; this.proto = proto; this.n = n; this.formals = formals; this.nFormals = formals.length; if (global instanceof ESGlobal) this.global = (ESGlobal) global; if (global != null) { this.scopeLength = 1; this.scope = new ESBase[1]; this.scope[0] = global; } } protected ESClosure(ESBase []scope, int scopeLength) { super("Function", null); hasFields = true; this.scope = scope; this.scopeLength = scopeLength; } /** * Create a new object based on a prototype */ protected ESClosure() {} public void closure(Call env) { if (scope == null) { scope = new ESBase[16]; } for (; scopeLength < env.scopeLength; scopeLength++) scope[scopeLength] = env.scope[scopeLength]; if (scopeLength == 0) scope[scopeLength++] = env.global; global = (ESGlobal) scope[0]; } /** * Create a new object based on a prototype */ void setScope(ESBase []scope, int scopeLength) { this.scope = scope; this.scopeLength = scopeLength; } public ESBase hasProperty(ESString id) throws Throwable { if (id.equals(PROTOTYPE)) { if (proto == null) { ESObject obj = Global.getGlobalProto().createObject(); proto = obj; obj.put(CONSTRUCTOR, this, DONT_ENUM); } return proto; } else if (id.equals(LENGTH)) { return ESNumber.create(nFormals); } else if (hasFields) { return super.hasProperty(id); } else if (prototype != null) return prototype.hasProperty(id); else return ESBase.esEmpty; } public ESBase getProperty(ESString id) throws Throwable { if (id.equals(PROTOTYPE)) { if (proto == null) { ESObject obj = Global.getGlobalProto().createObject(); proto = obj; obj.put(CONSTRUCTOR, this, DONT_ENUM); } return proto; } else if (id.equals(LENGTH)) { return ESNumber.create(nFormals); } else if (hasFields) { return super.getProperty(id); } else return prototype.getProperty(id); } public boolean canPut(ESString id) { if (id.equals(PROTOTYPE)) { return true; } else if (id.equals(LENGTH)) { return false; } else if (hasFields) { return super.canPut(id); } else { return true; } } public void setProperty(ESString id, ESBase value) throws Throwable { if (id.equals(PROTOTYPE)) { proto = value; } else if (id.equals(LENGTH)) { } else if (hasFields) { super.setProperty(id, value); } else { init(className, prototype); hasFields = true; super.setProperty(id, value); } } public void put(ESString id, ESBase value, int flags) { if (id.equals(PROTOTYPE)) { proto = value; } else if (id.equals(LENGTH)) { } else if (hasFields) { super.put(id, value, flags); } else { init(className, prototype); hasFields = true; super.put(id, value, flags); } } public ESBase delete(ESString id) throws Throwable { if (id.equals(PROTOTYPE)) { proto = ESBase.esEmpty; return ESBoolean.TRUE; } else if (id.equals(LENGTH)) { return ESBoolean.FALSE; } else if (hasFields) { return super.delete(id); } else return ESBoolean.TRUE; } public ESString toStr() { return ESString.create(decompile()); } String decompile() { StringBuffer sbuf = new StringBuffer(); sbuf.append("function "); if (name != null && ! name.toString().startsWith("$lambda")) sbuf.append(name); sbuf.append("("); for (int i = 0; formals != null && i < nFormals; i++) { if (i != 0) sbuf.append(", "); sbuf.append(formals[i]); } sbuf.append(") "); sbuf.append("{ "); sbuf.append("[compiled code]"); sbuf.append(" }"); return sbuf.toString(); } protected ESBase dispatch() throws ESException { throw new ESException("dispatch not specialized"); } public ESBase call(Call call, int length) throws Throwable { if (global != null) call.global = global; if (esClass != null) return esClass.call(n, call, length); else return ESBase.esUndefined; } public ESBase construct(Call eval, int length) throws Throwable { Global resin = Global.getGlobalProto(); ESObject obj = Global.getGlobalProto().createObject(); ESBase proto = this.proto; if (! (proto instanceof ESObject)) proto = resin.object.getProperty(PROTOTYPE); if (proto instanceof ESObject) obj.prototype = proto; eval.setThis(obj); ESBase value = call(eval, length); return value instanceof ESObject ? (ESObject) value : obj; } public ESBase typeof() throws ESException { return ESString.create("function"); } protected void copy(Object newObj) { ESClosure closure = (ESClosure) newObj; super.copy(newObj); closure.name = name; closure.esClass = esClass; closure.n = n; closure.formals = formals; closure.nFormals = nFormals; closure.stackRequired = stackRequired; closure.scopeRequired = scopeRequired; closure.scopeLength = scopeLength; closure.scope = new ESBase[scopeLength]; closure.hasFields = hasFields; for (int i = 0; i < scopeLength; i++) { if (scope[i] != null) { closure.scope[i] = (ESBase) scope[i]; } } closure.proto = proto; // XXX: should be copied? } public ESObject dup() { return new ESClosure(); } ESObject resinCopy() { ESObject obj = dup(); copy(obj); return obj; } ESObject getClassPrototype() throws Throwable { ESBase proto = hasProperty(PROTOTYPE); return (ESObject) proto; } void setClassPrototype(ESObject proto) throws ESException { this.proto = proto; proto.put(CONSTRUCTOR, this, DONT_ENUM); } }