1 28 29 package com.caucho.es; 30 31 import com.caucho.es.parser.Parser; 32 import com.caucho.vfs.ReadStream; 33 import com.caucho.vfs.Vfs; 34 35 import java.io.IOException ; 36 import java.util.ArrayList ; 37 38 41 class NativeFunction extends Native { 42 static final int NEW = 1; 43 static final int TO_STRING = NEW + 1; 44 static final int CALL = TO_STRING + 1; 45 static final int APPLY = CALL + 1; 46 47 static ESId LENGTH = ESId.intern("length"); 48 49 52 private NativeFunction(String name, int n, int len) 53 { 54 super(name, len); 55 56 this.n = n; 57 } 58 59 64 static NativeWrapper create(Global resin) 65 { 66 ESBase []scope = new ESBase[] { resin.getGlobalProto() }; 67 ESClosure funProto = new ESClosure(scope, 1); 68 funProto.name = ESId.intern("Function"); 69 70 Native natFunction = new NativeFunction("Function", NEW, 1); 71 NativeWrapper function = new NativeWrapper(resin, natFunction, 72 funProto, ESThunk.FUN_THUNK); 73 resin.funProto = funProto; 74 75 put(funProto, "toString", TO_STRING, 0); 76 put(funProto, "call", CALL, 1); 77 put(funProto, "apply", APPLY, 2); 78 funProto.prototype = resin.objProto; 79 80 funProto.setClean(); 81 function.setClean(); 82 83 return function; 84 } 85 86 private static void put(ESObject proto, String name, int n, int len) 87 { 88 ESId id = ESId.intern(name); 89 90 proto.put(id, new NativeFunction(name, n, len), DONT_ENUM); 91 } 92 93 public ESBase call(Call eval, int length) throws Throwable 94 { 95 switch (n) { 96 case NEW: 97 return createAnonymous(eval, length); 98 99 case TO_STRING: 101 if (eval.getThis() instanceof ESClosure) { 103 ESClosure closure = (ESClosure) eval.getThis(); 104 105 return ESString.create(closure.decompile()); 106 } else if (eval.getThis() instanceof NativeWrapper) { 107 NativeWrapper wrapper = (NativeWrapper) eval.getThis(); 108 109 return wrapper.fun.toStr(); 110 } else 111 throw new ESException("to string bound to function: " + 112 eval.getThis().getClass()); 113 114 case CALL: 115 int oldTop = eval.top; 116 ESBase fun = eval.getArg(-1); 117 ESBase callThis = null; 118 119 try { 120 if (length > 0) { 121 callThis = eval.getArg(0); 122 } else 123 callThis = esNull; 124 125 if (callThis == esNull || callThis == esUndefined || 126 callThis == esEmpty) 127 eval.setArg(0, eval.getGlobal()); 128 else 129 eval.setArg(0, callThis.toObject()); 130 eval.top++; 131 132 return fun.call(eval, length > 0 ? length - 1 : 0); 133 } finally { 134 eval.top = oldTop; 135 } 136 137 case APPLY: 138 return apply(eval, length); 139 140 default: 141 throw new ESException("Unknown object function"); 142 } 143 } 144 145 148 private ESClosure parseFunction(Call eval, int length) throws Throwable 149 { 150 StringBuffer sbuf = new StringBuffer (); 151 152 sbuf.append("function anonymous("); 153 ArrayList argList = new ArrayList (); 154 for (int i = 0; i < length - 1; i++) { 155 if (i != 0) 156 sbuf.append(","); 157 String str = eval.getArg(i).toString(); 158 int j = 0; 159 int p = 0; 160 161 while ((p = str.indexOf(',', j)) >= 0 || 162 (p = str.indexOf(' ', j)) >= 0) { 163 if (j < p) 164 argList.add(ESId.intern(str.substring(j, p))); 165 j = p + 1; 166 } 167 if (j < str.length()) 168 argList.add(ESId.intern(str.substring(j))); 169 170 sbuf.append(str); 171 } 172 ESId []args = new ESId[argList.size()]; 173 argList.toArray(args); 174 sbuf.append("){"); 175 if (length > 0) 176 sbuf.append(eval.getArg(length - 1).toString()); 177 sbuf.append("}\n"); 178 sbuf.append("return anonymous();"); 179 180 Global resin = Global.getGlobalProto(); 181 Script script = null; 182 try { 183 Parser parser = new Parser(); 184 ReadStream is = Vfs.openString(sbuf.toString()); 185 script = parser.parse(is, "anonymous", 1); 186 is.close(); 187 } catch (IOException e) { 188 e.printStackTrace(); 189 } 190 191 ESCallable jsClass = script.initClass(resin, eval.getGlobal()); 192 193 ESClosure fun = new ESClosure(ESId.intern("anonymous"), jsClass, 195 null, 2, args, eval.getGlobal()); 196 197 return fun; 198 } 199 200 private ESBase createAnonymous(Call eval, int length) throws Throwable 201 { 202 return parseFunction(eval, length); 203 } 204 205 private ESBase apply(Call eval, int length) throws Throwable 206 { 207 Global resin = Global.getGlobalProto(); 208 Call call = eval.getCall(); 209 210 call.top = 1; 211 call.global = eval.global; 212 call.caller = eval; 213 214 ESBase fun = eval.getArg(-1); 215 ESBase callThis = null; 216 217 if (length > 0) { 218 callThis = eval.getArg(0); 219 } else 220 callThis = esNull; 221 222 if (callThis == esNull || callThis == esUndefined || 223 callThis == esEmpty) 224 call.setArg(-1, eval.getGlobal()); 225 else 226 call.setArg(-1, callThis.toObject()); 227 228 int j = 0; 229 for (int i = 1; i < length; i++) { 230 ESBase arg = eval.getArg(i); 231 232 if (arg == esNull || arg == esUndefined || arg == esEmpty) 233 continue; 234 235 ESBase arglen = arg.hasProperty(LENGTH); 236 237 if (arglen == null) 238 call.setArg(j++, arg); 239 else { 240 int len = arglen.toInt32(); 241 242 if (j + len > call.stack.length - 2) 243 throw new ESException("stack overflow"); 244 245 for (int k = 0; k < len; k++) 246 call.setArg(j++, arg.getProperty(ESString.create(k))); 247 248 if (len < 0) 249 call.setArg(j++, arg); 250 } 251 } 252 253 ESBase value = fun.call(call, j); 254 255 resin.freeCall(call); 256 257 return value; 258 } 259 } 260 | Popular Tags |