001: /*
002: * Copyright (c) 1998-2008 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: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.quercus.env;
031:
032: import com.caucho.quercus.QuercusException;
033: import com.caucho.quercus.annotation.*;
034: import com.caucho.quercus.expr.Expr;
035: import com.caucho.quercus.expr.ExprFactory;
036: import com.caucho.quercus.function.Marshal;
037: import com.caucho.quercus.function.MarshalFactory;
038: import com.caucho.quercus.module.ModuleContext;
039: import com.caucho.quercus.parser.QuercusParser;
040: import com.caucho.util.L10N;
041:
042: import java.lang.annotation.Annotation;
043:
044: /**
045: * Represents the introspected static function information.
046: */
047: abstract public class JavaInvoker extends AbstractJavaMethod {
048: private static final L10N L = new L10N(JavaInvoker.class);
049:
050: private static final Object[] NULL_ARGS = new Object[0];
051: private static final Value[] NULL_VALUES = new Value[0];
052:
053: private final ModuleContext _moduleContext;
054: private final String _name;
055: private final Class[] _param;
056: private final Class _retType;
057: private final Annotation[][] _paramAnn;
058: private final Annotation[] _methodAnn;
059:
060: private volatile boolean _isInit;
061:
062: private int _minArgumentLength;
063: private int _maxArgumentLength;
064:
065: private boolean _hasEnv;
066: private boolean _hasThis;
067: private Expr[] _defaultExprs;
068: private Marshal[] _marshalArgs;
069: private boolean _hasRestArgs;
070: private Marshal _unmarshalReturn;
071:
072: private boolean _isRestReference;
073:
074: private boolean _isCallUsesVariableArgs;
075: private boolean _isCallUsesSymbolTable;
076:
077: /**
078: * Creates the statically introspected function.
079: */
080: public JavaInvoker(ModuleContext moduleContext, String name,
081: Class[] param, Annotation[][] paramAnn,
082: Annotation[] methodAnn, Class retType) {
083: _moduleContext = moduleContext;
084: _name = name;
085: _param = param;
086: _paramAnn = paramAnn;
087: _methodAnn = methodAnn;
088: _retType = retType;
089:
090: // init();
091: }
092:
093: public synchronized void init() {
094: if (_isInit)
095: return;
096:
097: MarshalFactory marshalFactory = _moduleContext
098: .getMarshalFactory();
099: ExprFactory exprFactory = _moduleContext.getExprFactory();
100:
101: try {
102: boolean callUsesVariableArgs = false;
103: boolean callUsesSymbolTable = false;
104: boolean returnNullAsFalse = false;
105:
106: for (Annotation ann : _methodAnn) {
107: if (VariableArguments.class.isAssignableFrom(ann
108: .annotationType()))
109: callUsesVariableArgs = true;
110:
111: if (UsesSymbolTable.class.isAssignableFrom(ann
112: .annotationType()))
113: callUsesSymbolTable = true;
114:
115: if (ReturnNullAsFalse.class.isAssignableFrom(ann
116: .annotationType()))
117: returnNullAsFalse = true;
118: }
119:
120: _isCallUsesVariableArgs = callUsesVariableArgs;
121: _isCallUsesSymbolTable = callUsesSymbolTable;
122:
123: _hasEnv = _param.length > 0 && _param[0].equals(Env.class);
124: int envOffset = _hasEnv ? 1 : 0;
125:
126: if (envOffset < _param.length)
127: _hasThis = hasThis(_param[envOffset],
128: _paramAnn[envOffset]);
129: else
130: _hasThis = false;
131:
132: if (_hasThis)
133: envOffset++;
134:
135: boolean hasRestArgs = false;
136: boolean isRestReference = false;
137:
138: if (_param.length > 0
139: && (_param[_param.length - 1].equals(Value[].class) || _param[_param.length - 1]
140: .equals(Object[].class))) {
141: hasRestArgs = true;
142:
143: for (Annotation ann : _paramAnn[_param.length - 1]) {
144: if (Reference.class.isAssignableFrom(ann
145: .annotationType()))
146: isRestReference = true;
147: }
148: }
149:
150: _hasRestArgs = hasRestArgs;
151: _isRestReference = isRestReference;
152:
153: int argLength = _param.length;
154:
155: if (_hasRestArgs)
156: argLength -= 1;
157:
158: _defaultExprs = new Expr[argLength - envOffset];
159: _marshalArgs = new Marshal[argLength - envOffset];
160:
161: _maxArgumentLength = argLength - envOffset;
162: _minArgumentLength = _maxArgumentLength;
163:
164: for (int i = 0; i < argLength - envOffset; i++) {
165: boolean isReference = false;
166: boolean isNotNull = false;
167:
168: for (Annotation ann : _paramAnn[i + envOffset]) {
169: if (Optional.class.isAssignableFrom(ann
170: .annotationType())) {
171: _minArgumentLength--;
172:
173: Optional opt = (Optional) ann;
174:
175: if (!opt.value().equals("")) {
176: Expr expr = QuercusParser.parseDefault(opt
177: .value());
178:
179: _defaultExprs[i] = expr;
180: } else
181: _defaultExprs[i] = exprFactory
182: .createDefault();
183: } else if (Reference.class.isAssignableFrom(ann
184: .annotationType())) {
185: isReference = true;
186: } else if (NotNull.class.isAssignableFrom(ann
187: .annotationType())) {
188: isNotNull = true;
189: }
190: }
191:
192: Class argType = _param[i + envOffset];
193:
194: if (isReference) {
195: _marshalArgs[i] = marshalFactory.createReference();
196:
197: if (!Value.class.equals(argType)
198: && !Var.class.equals(argType)) {
199: throw new QuercusException(
200: L
201: .l(
202: "reference must be Value or Var for {0}",
203: _name));
204: }
205: } else
206: _marshalArgs[i] = marshalFactory.create(argType,
207: isNotNull);
208: }
209:
210: _unmarshalReturn = marshalFactory.create(_retType, false,
211: returnNullAsFalse);
212: } finally {
213: _isInit = true;
214: }
215: }
216:
217: /**
218: * Returns the minimally required number of arguments.
219: */
220: @Override
221: public int getMinArgLength() {
222: if (!_isInit)
223: init();
224: return _minArgumentLength;
225: }
226:
227: /**
228: * Returns the maximum number of arguments allowed.
229: */
230: @Override
231: public int getMaxArgLength() {
232: if (!_isInit)
233: init();
234: return _maxArgumentLength;
235: }
236:
237: /**
238: * Returns true if the environment is an argument.
239: */
240: public boolean getHasEnv() {
241: if (!_isInit)
242: init();
243:
244: return _hasEnv;
245: }
246:
247: /**
248: * Returns true if the environment has rest-style arguments.
249: */
250: public boolean getHasRestArgs() {
251: if (!_isInit)
252: init();
253:
254: return _hasRestArgs;
255: }
256:
257: /**
258: * Returns true if the rest argument is a reference.
259: */
260: public boolean isRestReference() {
261: if (!_isInit)
262: init();
263:
264: return _isRestReference;
265: }
266:
267: /**
268: * Returns the unmarshaller for the return
269: */
270: public Marshal getUnmarshalReturn() {
271: if (!_isInit)
272: init();
273:
274: return _unmarshalReturn;
275: }
276:
277: /**
278: * Returns true if the call uses variable arguments.
279: */
280: @Override
281: public boolean isCallUsesVariableArgs() {
282: if (!_isInit)
283: init();
284:
285: return _isCallUsesVariableArgs;
286: }
287:
288: /**
289: * Returns true if the call uses the symbol table
290: */
291: @Override
292: public boolean isCallUsesSymbolTable() {
293: if (!_isInit)
294: init();
295:
296: return _isCallUsesSymbolTable;
297: }
298:
299: /**
300: * Returns true if the result is a boolean.
301: */
302: public boolean isBoolean() {
303: if (!_isInit)
304: init();
305:
306: return _unmarshalReturn.isBoolean();
307: }
308:
309: /**
310: * Returns true if the result is a string.
311: */
312: public boolean isString() {
313: if (!_isInit)
314: init();
315:
316: return _unmarshalReturn.isString();
317: }
318:
319: /**
320: * Returns true if the result is a long.
321: */
322: public boolean isLong() {
323: if (!_isInit)
324: init();
325:
326: return _unmarshalReturn.isLong();
327: }
328:
329: /**
330: * Returns true if the result is a double.
331: */
332: public boolean isDouble() {
333: if (!_isInit)
334: init();
335:
336: return _unmarshalReturn.isDouble();
337: }
338:
339: public String getName() {
340: return _name;
341: }
342:
343: /**
344: * Returns the marshal arguments.
345: */
346: protected Marshal[] getMarshalArgs() {
347: if (!_isInit)
348: init();
349:
350: return _marshalArgs;
351: }
352:
353: /**
354: * Returns the parameter annotations.
355: */
356: protected Annotation[][] getParamAnn() {
357: if (!_isInit)
358: init();
359:
360: return _paramAnn;
361: }
362:
363: /**
364: * Returns the default expressions.
365: */
366: protected Expr[] getDefaultExprs() {
367: if (!_isInit)
368: init();
369:
370: return _defaultExprs;
371: }
372:
373: /**
374: * Evaluates a function's argument, handling ref vs non-ref
375: */
376: @Override
377: public Value[] evalArguments(Env env, Expr fun, Expr[] args) {
378: if (!_isInit)
379: init();
380:
381: Value[] values = new Value[args.length];
382:
383: for (int i = 0; i < args.length; i++) {
384: Marshal arg = null;
385:
386: if (i < _marshalArgs.length)
387: arg = _marshalArgs[i];
388: else if (_isRestReference) {
389: values[i] = args[i].evalRef(env);
390: continue;
391: } else {
392: values[i] = args[i].eval(env);
393: continue;
394: }
395:
396: if (arg == null)
397: values[i] = args[i].eval(env).copy();
398: else if (arg.isReference())
399: values[i] = args[i].evalRef(env);
400: else {
401: // php/0d04
402: values[i] = args[i].eval(env);
403: }
404: }
405:
406: return values;
407: }
408:
409: /**
410: * Returns the cost of marshaling for this method.
411: */
412: public int getMarshalingCost(Value[] args) {
413: if (!_isInit)
414: init();
415:
416: if (_hasRestArgs) {
417: } else if (args.length < _marshalArgs.length) {
418: // not enough args
419: return Integer.MAX_VALUE;
420: } else if (args.length > _marshalArgs.length) {
421: // too many args
422: return Integer.MAX_VALUE;
423: }
424:
425: int cost = 0;
426: int i = 0;
427:
428: for (; i < _marshalArgs.length; i++) {
429: Marshal marshal = _marshalArgs[i];
430:
431: if (i < args.length && args[i] != null) {
432: Value arg = args[i];
433:
434: int argCost = marshal.getMarshalingCost(arg);
435:
436: cost = Math.max(argCost + cost, cost);
437: }
438: }
439:
440: // consume all the REST args
441: if (_hasRestArgs) {
442: int restLen = args.length - _marshalArgs.length;
443:
444: if (restLen > 0)
445: i += restLen;
446: }
447:
448: // too many args passed in
449: if (i != args.length)
450: return Integer.MAX_VALUE;
451:
452: return cost;
453: }
454:
455: public Value callMethod(Env env, Value qThis, Expr[] exprs) {
456: if (!_isInit)
457: init();
458:
459: int len = (_defaultExprs.length + (_hasEnv ? 1 : 0)
460: + (_hasThis ? 1 : 0) + (_hasRestArgs ? 1 : 0));
461:
462: Object[] values = new Object[len];
463:
464: int k = 0;
465:
466: if (_hasEnv)
467: values[k++] = env;
468: Object obj = null;
469: if (_hasThis) {
470: values[k++] = qThis;
471: } else if (qThis != null)
472: obj = qThis.toJavaObject();
473:
474: for (int i = 0; i < _marshalArgs.length; i++) {
475: Expr expr;
476:
477: if (i < exprs.length && exprs[i] != null)
478: expr = exprs[i];
479: else {
480: expr = _defaultExprs[i];
481:
482: if (expr == null)
483: expr = _moduleContext.getExprFactory()
484: .createRequired();
485: }
486:
487: values[k] = _marshalArgs[i].marshal(env, expr, _param[k]);
488:
489: k++;
490: }
491:
492: if (_hasRestArgs) {
493: Value[] rest;
494:
495: int restLen = exprs.length - _marshalArgs.length;
496:
497: if (restLen <= 0)
498: rest = NULL_VALUES;
499: else {
500: rest = new Value[restLen];
501:
502: for (int i = _marshalArgs.length; i < exprs.length; i++) {
503: if (_isRestReference)
504: rest[i - _marshalArgs.length] = exprs[i]
505: .evalRef(env);
506: else
507: rest[i - _marshalArgs.length] = exprs[i]
508: .eval(env);
509: }
510: }
511:
512: values[values.length - 1] = rest;
513: }
514:
515: Object result = invoke(obj, values);
516:
517: return _unmarshalReturn.unmarshal(env, result);
518: }
519:
520: public Value call(Env env, Value[] args) {
521: return callMethod(env, null, args);
522: }
523:
524: public Value callMethod(Env env, Value qThis, Value[] args) {
525: if (!_isInit)
526: init();
527:
528: int len = _param.length;
529:
530: Object[] javaArgs = new Object[len];
531:
532: int k = 0;
533:
534: if (_hasEnv)
535: javaArgs[k++] = env;
536:
537: Object obj = qThis != null ? qThis.toJavaObject() : null;
538:
539: if (_hasThis) {
540: javaArgs[k++] = qThis;
541: }
542:
543: for (int i = 0; i < _marshalArgs.length; i++) {
544:
545: if (i < args.length && args[i] != null)
546: javaArgs[k] = _marshalArgs[i].marshal(env, args[i],
547: _param[k]);
548: else if (_defaultExprs[i] != null) {
549: javaArgs[k] = _marshalArgs[i].marshal(env,
550: _defaultExprs[i], _param[k]);
551: } else {
552: env
553: .warning(L
554: .l(
555: "function '{0}' has {1} required arguments, but only {2} were provided",
556: _name, _minArgumentLength,
557: args.length));
558:
559: //return NullValue.NULL;
560:
561: javaArgs[k] = _marshalArgs[i].marshal(env,
562: NullValue.NULL, _param[k]);
563: }
564:
565: k++;
566: }
567:
568: if (_hasRestArgs) {
569: Value[] rest;
570:
571: int restLen = args.length - _marshalArgs.length;
572:
573: if (restLen <= 0)
574: rest = NULL_VALUES;
575: else {
576: rest = new Value[restLen];
577:
578: for (int i = _marshalArgs.length; i < args.length; i++) {
579: if (_isRestReference)
580: rest[i - _marshalArgs.length] = args[i];
581: else
582: rest[i - _marshalArgs.length] = args[i]
583: .toValue();
584: }
585: }
586:
587: javaArgs[k++] = rest;
588: }
589:
590: Object result = invoke(obj, javaArgs);
591:
592: return _unmarshalReturn.unmarshal(env, result);
593: }
594:
595: abstract public Object invoke(Object obj, Object[] args);
596:
597: //
598: // Utility methods
599: //
600: private boolean hasThis(Class param, Annotation[] ann) {
601: if (!param.isAssignableFrom(ObjectValue.class))
602: return false;
603:
604: for (int i = 0; i < ann.length; i++) {
605: if (This.class.isAssignableFrom(ann[i].annotationType()))
606: return true;
607: }
608:
609: return false;
610: }
611: }
|