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: import com.caucho.es.parser.Parser;
032: import com.caucho.es.wrapper.Wrapper;
033: import com.caucho.java.LineMap;
034: import com.caucho.loader.DynamicClassLoader;
035: import com.caucho.loader.SimpleLoader;
036: import com.caucho.log.Log;
037: import com.caucho.util.FreeList;
038: import com.caucho.util.IntMap;
039: import com.caucho.util.LruCache;
040: import com.caucho.vfs.Path;
041:
042: import java.lang.ref.SoftReference;
043: import java.util.Date;
044: import java.util.HashMap;
045: import java.util.logging.Logger;
046:
047: /**
048: * Implementation class for the global prototype
049: */
050: public class Global extends ESBase {
051: private static Integer LOCK = new Integer(0);
052: private static final Logger log = Log.open(Global.class);
053:
054: private final static int OBJECT = 0;
055: private final static int FUNCTION = OBJECT + 1;
056: private final static int ARRAY = FUNCTION + 1;
057: private final static int STRING = ARRAY + 1;
058: private final static int BOOL = STRING + 1;
059: private final static int NUM = BOOL + 1;
060: private final static int DATE = NUM + 1;
061: private final static int MATH = DATE + 1;
062: private final static int REGEXP = MATH + 1;
063: private final static int PACKAGES = REGEXP + 1;
064: private final static int CAUCHO = PACKAGES + 1;
065: private final static int JAVA = CAUCHO + 1;
066: private final static int JAVAX = JAVA + 1;
067:
068: private static Global goldGlobal;
069: private static IntMap propertyMap;
070:
071: private ESGlobal global;
072: private Global root;
073:
074: ESObject objProto;
075: ESObject object;
076:
077: ESObject funProto;
078: ESObject fun;
079:
080: ESObject arrayProto;
081: ESObject array;
082:
083: ESObject stringProto;
084: ESObject string;
085:
086: ESObject boolProto;
087: ESObject bool;
088:
089: ESObject numProto;
090: ESObject num;
091:
092: ESObject dateProto;
093: ESObject date;
094:
095: ESObject math;
096:
097: ESRegexp regexpProto;
098: ESRegexpWrapper regExp;
099:
100: ESPackage pkg;
101:
102: HashMap properties;
103: HashMap globalProperties;
104:
105: // static lookup of current Global
106:
107: private static final ThreadLocal<Global> _globals = new ThreadLocal<Global>();
108:
109: private static Thread lastThread;
110: private static Global lastGlobal;
111:
112: // bean wrapping
113:
114: private final static LruCache<Class, ESBase> _staticWraps = new LruCache<Class, ESBase>(
115: 256);
116: private final static LruCache<Class, ESBase> _staticClassWraps = new LruCache<Class, ESBase>(
117: 256);
118:
119: // cache
120:
121: private static FreeList<Call> _freeCalls = new FreeList<Call>(2);
122:
123: private static HashMap<String, SoftReference<Script>> _runtimeScripts;
124: /* = new HashMap<String,SoftReference<Script>>(); */
125:
126: private ClassLoader parentLoader;
127: private ClassLoader loader;
128: private Path scriptPath;
129: private Path classDir;
130: private HashMap importScripts;
131: private HashMap importGlobals;
132:
133: int markCount;
134:
135: /**
136: * Null constructor
137: */
138: private Global(boolean init) {
139: ESBase.init(null);
140:
141: /*
142: if (_globals == null) {
143: _globals = new ThreadLocal();
144: _staticWraps = new LruCache<Class,ESBase>(256);
145: _staticClassWraps = new LruCache<Class,ESBase>(256);
146: ESId.intern("foo"); // XXX: bogus to fix stupid kaffe static init.
147: }
148: */
149:
150: propertyMap = new IntMap();
151: propertyMap.put(ESId.intern("Object"), OBJECT);
152: propertyMap.put(ESId.intern("Function"), FUNCTION);
153: propertyMap.put(ESId.intern("Array"), ARRAY);
154: propertyMap.put(ESId.intern("String"), STRING);
155: propertyMap.put(ESId.intern("Boolean"), BOOL);
156: propertyMap.put(ESId.intern("Number"), NUM);
157: propertyMap.put(ESId.intern("Date"), DATE);
158: propertyMap.put(ESId.intern("Math"), MATH);
159: propertyMap.put(ESId.intern("RegExp"), REGEXP);
160: propertyMap.put(ESId.intern("Packages"), PACKAGES);
161: propertyMap.put(ESId.intern("caucho"), CAUCHO);
162: propertyMap.put(ESId.intern("java"), JAVA);
163: propertyMap.put(ESId.intern("javax"), JAVAX);
164:
165: globalProperties = new HashMap();
166:
167: object = NativeObject.create(this );
168: fun = NativeFunction.create(this );
169: object.prototype = funProto;
170:
171: int flags = ESBase.DONT_ENUM;
172: int allflags = (ESBase.DONT_ENUM | ESBase.DONT_DELETE | ESBase.READ_ONLY);
173:
174: array = NativeArray.create(this );
175: string = NativeString.create(this );
176: bool = NativeBoolean.create(this );
177: num = NativeNumber.create(this );
178: math = NativeMath.create(this );
179: date = NativeDate.create(this );
180:
181: regExp = NativeRegexp.create(this );
182:
183: pkg = ESPackage.create();
184:
185: NativeGlobal.create(this );
186: NativeFile.create(this );
187:
188: globalProperties.put(ESId.intern("NaN"), ESNumber
189: .create(0.0 / 0.0));
190: globalProperties.put(ESId.intern("Infinity"), ESNumber
191: .create(1.0 / 0.0));
192: }
193:
194: /**
195: * Creates a new global object for a script thread.
196: *
197: * @param properties any global properties for the script
198: * @param proto a Java prototype object underlying the global object
199: * @param classDir work directory where generated classes will go
200: * @param scriptPath a path for searching scripts
201: * @param parentLoader the parent class loader.
202: */
203: Global(HashMap properties, Object proto, Path classDir,
204: Path scriptPath, ClassLoader parentLoader) throws Throwable {
205: synchronized (LOCK) {
206: if (goldGlobal == null)
207: goldGlobal = new Global(true);
208: }
209:
210: root = this ;
211: this .parentLoader = parentLoader;
212: this .loader = SimpleLoader.create(parentLoader, classDir, null);
213: this .classDir = classDir;
214: this .scriptPath = scriptPath;
215:
216: // Object
217: objProto = (ESObject) goldGlobal.objProto.resinCopy();
218: object = (ESObject) goldGlobal.object.resinCopy();
219:
220: // Function
221:
222: funProto = (ESObject) goldGlobal.funProto.resinCopy();
223: funProto.prototype = objProto;
224: object.prototype = funProto;
225:
226: fun = (ESObject) goldGlobal.fun.resinCopy();
227: fun.prototype = funProto;
228:
229: // Array
230:
231: arrayProto = (ESObject) goldGlobal.arrayProto.resinCopy();
232: arrayProto.prototype = objProto;
233:
234: array = (ESObject) goldGlobal.array.resinCopy();
235: array.prototype = funProto;
236:
237: // String
238:
239: stringProto = (ESObject) goldGlobal.stringProto.resinCopy();
240: stringProto.prototype = objProto;
241:
242: string = (ESObject) goldGlobal.string.resinCopy();
243: string.prototype = funProto;
244:
245: // Boolean
246:
247: boolProto = (ESObject) goldGlobal.boolProto.resinCopy();
248: boolProto.prototype = objProto;
249:
250: bool = (ESObject) goldGlobal.bool.resinCopy();
251: bool.prototype = funProto;
252:
253: // Number
254:
255: numProto = (ESObject) goldGlobal.numProto.resinCopy();
256: numProto.prototype = objProto;
257:
258: num = (ESObject) goldGlobal.num.resinCopy();
259: num.prototype = funProto;
260:
261: // Math
262:
263: math = (ESObject) goldGlobal.math.resinCopy();
264: math.prototype = objProto;
265:
266: // Date
267:
268: dateProto = (ESObject) goldGlobal.dateProto.resinCopy();
269: dateProto.prototype = objProto;
270:
271: date = (ESObject) goldGlobal.date.resinCopy();
272: date.prototype = funProto;
273:
274: // RegExp
275:
276: //regexpProto = (ESRegexp) goldGlobal.regexpProto.resinCopy();
277: //regexpProto.prototype = objProto;
278:
279: //regExp = (ESRegexpWrapper) goldGlobal.regExp.resinCopy();
280: //regExp.prototype = funProto;
281: //regExp.regexp = regexpProto;
282:
283: pkg = ESPackage.create();
284:
285: if (proto != null) {
286: prototype = objectWrap(proto);
287: prototype.prototype = objProto;
288: } else
289: prototype = objProto;
290:
291: if (properties != null)
292: this .properties = properties;
293:
294: globalProperties = goldGlobal.globalProperties;
295: }
296:
297: Global(Global root) {
298: this .root = root;
299:
300: objProto = root.objProto;
301: object = root.object;
302: funProto = root.funProto;
303: fun = root.fun;
304: arrayProto = root.arrayProto;
305: array = root.array;
306: stringProto = root.stringProto;
307: string = root.string;
308: boolProto = root.boolProto;
309: bool = root.bool;
310: numProto = root.numProto;
311: num = root.num;
312: math = root.math;
313: dateProto = root.dateProto;
314: date = root.date;
315: regexpProto = root.regexpProto;
316: regExp = root.regExp;
317: pkg = root.pkg;
318: properties = root.properties;
319: prototype = root.prototype;
320: globalProperties = root.globalProperties;
321:
322: _runtimeScripts = root._runtimeScripts;
323: }
324:
325: void addProperty(ESId id, ESBase value) {
326: globalProperties.put(id, value);
327: }
328:
329: public ESBase getProperty(ESString id) throws Throwable {
330: int index = propertyMap.get(id);
331:
332: switch (index) {
333: case OBJECT:
334: return snap(id, object);
335:
336: case FUNCTION:
337: return snap(id, fun);
338:
339: case ARRAY:
340: return snap(id, array);
341:
342: case STRING:
343: return snap(id, string);
344:
345: case BOOL:
346: return snap(id, bool);
347:
348: case NUM:
349: return snap(id, num);
350:
351: case DATE:
352: return snap(id, date);
353:
354: case MATH:
355: return snap(id, math);
356:
357: case REGEXP:
358: return snap(id, getRegexp());
359:
360: case PACKAGES:
361: return snap(id, pkg);
362:
363: case CAUCHO:
364: return snap(id, pkg.getProperty("com")
365: .getProperty("caucho"));
366:
367: case JAVA:
368: return snap(id, pkg.getProperty("java"));
369:
370: case JAVAX:
371: return snap(id, pkg.getProperty("javax"));
372:
373: default:
374: ESBase value = prototype == null ? null : prototype
375: .getProperty(id);
376: Object obj;
377: if (value != null && value != esEmpty)
378: return snap(id, value);
379: else if (properties != null
380: && (obj = properties.get(id.toString())) != null)
381: return snap(id, objectWrap(obj));
382: else if ((value = (ESBase) globalProperties.get(id)) != null)
383: return snap(id, value);
384: else {
385: return esEmpty;
386: }
387: }
388: }
389:
390: private ESBase snap(ESString id, ESBase value) {
391: if (value == null)
392: throw new RuntimeException();
393:
394: global.put(id, value, DONT_ENUM);
395: return value;
396: }
397:
398: ESRegexpWrapper getRegexp() {
399: if (regExp != null)
400: return regExp;
401:
402: else if (root.regExp != null) {
403: regExp = root.regExp;
404: return regExp;
405: }
406:
407: initRegexp();
408:
409: return regExp;
410: }
411:
412: ESRegexp getRegexpProto() {
413: if (regexpProto != null)
414: return regexpProto;
415:
416: else if (root.regexpProto != null) {
417: regexpProto = root.regexpProto;
418: return regexpProto;
419: }
420:
421: initRegexp();
422:
423: return regexpProto;
424: }
425:
426: private void initRegexp() {
427:
428: root.regexpProto = (ESRegexp) goldGlobal.regexpProto
429: .resinCopy();
430: root.regexpProto.prototype = root.objProto;
431:
432: root.regExp = (ESRegexpWrapper) goldGlobal.regExp.resinCopy();
433: root.regExp.prototype = root.funProto;
434: root.regExp.regexp = root.regexpProto;
435:
436: regexpProto = root.regexpProto;
437: regExp = root.regExp;
438: }
439:
440: /**
441: * Sets a running script.
442: *
443: * @param name classname of the script.
444: * @param script the script itself.
445: */
446: public void addScript(String name, Script script) {
447: if (_runtimeScripts != null)
448: _runtimeScripts
449: .put(name, new SoftReference<Script>(script));
450: }
451:
452: /**
453: * Returns the line map for the named class to translate Java line
454: * numbers to javascript line numbers.
455: *
456: * @param className class throwing the error.
457: * @return the line map.
458: */
459: LineMap getLineMap(String className) {
460: try {
461: int p = className.indexOf('$');
462:
463: if (p > 0)
464: className = className.substring(0, p);
465:
466: Script script = null;
467:
468: if (_runtimeScripts != null) {
469: SoftReference<Script> ref = _runtimeScripts
470: .get(className);
471:
472: if (ref != null)
473: script = ref.get();
474: }
475:
476: if (script != null)
477: return script.getLineMap();
478: else
479: return null;
480: } catch (Exception e) {
481: return null;
482: }
483: }
484:
485: /**
486: * Returns the named script. If the script has already been loaded,
487: * return the old script.
488: */
489: Script findScript(String className) throws Throwable {
490: Script script = (Script) importScripts.get(className);
491:
492: if (script != null)
493: return script;
494:
495: Parser parser = new Parser();
496: parser.setScriptPath(getScriptPath());
497: parser.setClassLoader(getClassLoader());
498: parser.setWorkDir(getClassDir());
499:
500: return parser.parse(className);
501: }
502:
503: /**
504: * Returns the global prototype for the current thread.
505: */
506: public static Global getGlobalProto() {
507: return (Global) _globals.get();
508: }
509:
510: /**
511: * Starts execution of a JavaScript thread.
512: *
513: * @return the old global context for the thread.
514: */
515: Global begin() {
516: Global oldGlobal = (Global) _globals.get();
517: _globals.set(this );
518:
519: return oldGlobal;
520: }
521:
522: /**
523: * Completes execution of a JavaScript thread, restoring the global context.
524: *
525: * @param oldGlobal the old global context for the thread.
526: */
527: static void end(Global oldGlobal) {
528: _globals.set(oldGlobal);
529: }
530:
531: Call getCall() {
532: Call call = _freeCalls.allocate();
533: if (call == null)
534: return new Call();
535: else {
536: call.clear();
537: return call;
538: }
539: }
540:
541: void freeCall(Call call) {
542: call.free();
543: _freeCalls.free(call);
544: }
545:
546: ESBase objectWrap(Object object) throws Throwable {
547: if (object == null)
548: return ESBase.esNull;
549:
550: Class cl = object.getClass();
551: String clName = cl.getName();
552:
553: if (object instanceof ESBase)
554: return (ESBase) object;
555: if (clName.equals("java.lang.String"))
556: return new ESString(object.toString());
557: if (clName.equals("java.lang.Double"))
558: return ESNumber.create(((Double) object).doubleValue());
559: if (clName.equals("java.util.Date"))
560: return convertDate(object);
561:
562: ESBase wrapper;
563: synchronized (_staticWraps) {
564: wrapper = _staticWraps.get(cl);
565: }
566:
567: if (wrapper == null
568: || ((DynamicClassLoader) wrapper.getClass()
569: .getClassLoader()).isDestroyed()) {
570: ESBase[] values = Wrapper.bean(this , cl);
571:
572: if (values == null)
573: return ESBase.esNull;
574:
575: ESBase clWrapper = values[0];
576: wrapper = values[1];
577: if (wrapper.getClass().getClassLoader().getParent().equals(
578: getClass().getClassLoader())) {
579: synchronized (_staticWraps) {
580: _staticClassWraps.put(cl, clWrapper);
581: _staticWraps.put(cl, wrapper);
582: }
583: }
584: }
585:
586: if (wrapper instanceof ESJavaWrapper)
587: return ((ESJavaWrapper) wrapper).wrap(object);
588: else {
589: return ((ESBeanWrapper) wrapper).wrap(object);
590: }
591: }
592:
593: private ESBase convertDate(Object object) {
594: return ESDate.create(((Date) object).getTime());
595: }
596:
597: // XXX: backwards -- s/b wrap and staticWrap
598: public static ESBase wrap(Object object) throws Throwable {
599: return getGlobalProto().objectWrap(object);
600: }
601:
602: ESBase classWrap(Class cl) throws Throwable {
603: if (cl == null)
604: throw new RuntimeException();
605:
606: ESBase clWrapper;
607:
608: synchronized (_staticWraps) {
609: clWrapper = _staticClassWraps.get(cl);
610: }
611:
612: if (clWrapper == null
613: || ((DynamicClassLoader) clWrapper.getClass()
614: .getClassLoader()).isDestroyed()) {
615: ESBase[] values = Wrapper.bean(this , cl);
616: clWrapper = values[0];
617: ESBase wrapper = values[1];
618:
619: synchronized (_staticWraps) {
620: _staticWraps.put(cl, wrapper);
621: _staticClassWraps.put(cl, clWrapper);
622: }
623: }
624:
625: return clWrapper;
626: }
627:
628: public ClassLoader getClassLoader() {
629: return loader != null ? loader : root.loader;
630: }
631:
632: public ClassLoader getParentLoader() {
633: return parentLoader != null ? parentLoader : root.parentLoader;
634: }
635:
636: public Path getClassDir() {
637: return classDir != null ? classDir : root.classDir;
638: }
639:
640: public Path getScriptPath() {
641: return scriptPath != null ? scriptPath : root.scriptPath;
642: }
643:
644: public void importScript(ESObject global, String name)
645: throws Throwable {
646: if (importScripts == null) {
647: importScripts = new HashMap();
648: importGlobals = new HashMap();
649: }
650:
651: ESGlobal scriptGlobal = (ESGlobal) importGlobals.get(name);
652:
653: if (scriptGlobal == null) {
654: if (importScripts.get(name) != null)
655: return;
656:
657: Parser parser = new Parser();
658: parser.setScriptPath(getScriptPath());
659: parser.setClassLoader(getClassLoader());
660: parser.setWorkDir(getClassDir());
661:
662: Script script = parser.parse(name);
663:
664: importScripts.put(name, script);
665:
666: scriptGlobal = script.initClass(this );
667:
668: importGlobals.put(name, scriptGlobal);
669:
670: scriptGlobal.execute();
671: }
672:
673: scriptGlobal.export(global);
674: }
675:
676: ESGlobal getGlobal() {
677: return global;
678: }
679:
680: public void setGlobal(ESGlobal global) {
681: this .global = global;
682: }
683:
684: public Object toJavaObject() throws ESException {
685: Object o = prototype.toJavaObject();
686: return (o == null) ? this : o;
687: }
688:
689: /*
690: * Somewhat bogus for creating based on globals.
691: * "this" is the global
692: */
693:
694: public ESObject createObject() {
695: return new ESObject("Object", objProto);
696: }
697:
698: /**
699: * Somewhat bogus for creating based on globals.
700: * "this" is the global
701: */
702: ESArray createArray() {
703: ESArray array = new ESArray();
704:
705: array.prototype = arrayProto;
706:
707: return array;
708: }
709:
710: void clearMark() {
711: markCount = 0;
712: }
713:
714: int addMark() {
715: return ++markCount;
716: }
717: }
|