/* * 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; import com.caucho.util.IntMap; /** * JavaScript object */ class NativeObject extends Native { static final int TO_OBJECT = 2; static final int TO_STRING = TO_OBJECT + 1; static final int VALUE_OF = TO_STRING + 1; static final int TO_SOURCE = VALUE_OF + 1; static final int WATCH = TO_SOURCE + 1; static final int UNWATCH = WATCH + 1; /** * Create a new object based on a prototype */ private NativeObject(String name, int n, int len) { super(name, len); this.n = n; } /** * Creates the native Object object */ static ESObject create(Global resin) { Native nativeObj = new NativeObject("Object", TO_OBJECT, 1); resin.objProto = new ESObject("Object", esBase); NativeWrapper obj = new NativeWrapper(resin, nativeObj, resin.objProto, ESThunk.OBJ_THUNK); put(resin.objProto, "toString", TO_STRING, 0, DONT_ENUM); put(resin.objProto, "valueOf", VALUE_OF, 0, DONT_ENUM); put(resin.objProto, "toSource", TO_SOURCE, 0, DONT_ENUM); put(resin.objProto, "watch", WATCH, 0, DONT_ENUM); put(resin.objProto, "unwatch", UNWATCH, 0, DONT_ENUM); resin.objProto.setClean(); obj.setClean(); return obj; } private static void put(ESObject obj, String name, int n, int len, int flags) { ESId id = ESId.intern(name); NativeObject fun = new NativeObject(name, n, len); try { obj.put(id, fun, flags); } catch (Exception e) { throw new RuntimeException(); } } public ESBase call(Call eval, int length) throws Throwable { switch (n) { // Object prototype stuff case TO_STRING: // XXX: Is this correct? Test. ESBase arg = eval.getArg(-1); if (arg instanceof ESObject) return toString((ESObject) arg); else return toString(arg.toObject()); case VALUE_OF: arg = eval.getArg(-1); if (arg instanceof ESWrapper) { ESWrapper obj = (ESWrapper) arg; if (obj.value instanceof ESBase) return (ESBase) obj.value; else return obj.toStr(); } return arg; case TO_OBJECT: if (length <= 0 || (arg = eval.getArg(0)) == ESBase.esNull || arg == ESBase.esUndefined || arg == ESBase.esEmpty) return Global.getGlobalProto().createObject(); else if (length > 1) return createObjectLiteral(eval, length); else return arg.toObject(); case TO_SOURCE: arg = eval.getThis(); Global.getGlobalProto().clearMark(); IntMap map = new IntMap(); arg.toSource(map, true); return arg.toSource(map, false); case WATCH: if (length < 2) throw new ESException("watch expects two arguments"); ESBase obj = eval.getThis(); ESString key = eval.getArg(0).toStr(); ESBase fun = eval.getArg(1); if (! (fun instanceof ESClosure) && ! (fun instanceof Native)) throw new ESException("watch requires function"); ((ESObject) obj).watch(key, fun); return esUndefined; case UNWATCH: if (length < 1) throw new ESException("unwatch expects one argument"); obj = eval.getThis(); key = eval.getArg(0).toStr(); ((ESObject) obj).unwatch(key); return esUndefined; default: throw new RuntimeException("Unknown object function"); } } public ESBase construct(Call eval, int length) throws Throwable { if (n != TO_OBJECT) return super.construct(eval, length); if (length == 0 || eval.getArg(0) == esNull || eval.getArg(0) == esUndefined || eval.getArg(0) == esEmpty) { return Global.getGlobalProto().createObject(); } if (length > 1) { return createObjectLiteral(eval, length); } return eval.getArg(0).toObject(); } private ESBase createObjectLiteral(Call call, int length) throws Throwable { ESObject obj = Global.getGlobalProto().createObject(); for (int i = 0; i + 1 < length; i += 2) { ESString key = call.getArg(i, length).toStr(); ESBase value = call.getArg(i + 1, length); obj.setProperty(key, value); } return obj; } static public ESBase toString(ESObject obj) throws ESException { return ESString.create("[object " + obj.getClassName() + "]"); } }