001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit.javascript;
039:
040: import java.lang.reflect.Field;
041:
042: import org.mozilla.javascript.BaseFunction;
043: import org.mozilla.javascript.Context;
044: import org.mozilla.javascript.Scriptable;
045: import org.mozilla.javascript.ScriptableObject;
046:
047: /**
048: * Implements a workaround for Rhino bug https://bugzilla.mozilla.org/show_bug.cgi?id=374918.
049: * Once this bug is solved, this class can be safely removed. Unit tests will ensure that this works
050: * correctly.<br/>
051: * Bug Description:
052: * string primitive prototype is badly resolved when many top scopes are involved.<br/>
053: * Workaround:
054: * a custom prototype is placed on the String object. It wraps the original one and fix bad scope resolution
055: * when needed. For this purpose it needs to change the visibility of some Rhino private members to
056: * access them and is likely to get broken on Rhino version updates.
057: *
058: * @version $Revision: 2132 $
059: * @author Marc Guillemot
060: */
061: public class StringPrimitivePrototypeBugFixer implements Scriptable {
062: private static final Field FieldPrototypeProperty_;
063: private static final Field FieldContextLastInterpreterFrame_;
064: private static Field FieldInterpreterCallFrameScope_;
065:
066: static {
067: try {
068: // BaseFunction's prototypeProperty
069: FieldPrototypeProperty_ = BaseFunction.class
070: .getDeclaredField("prototypeProperty");
071: FieldPrototypeProperty_.setAccessible(true);
072: // Context's lastInterpreterFrame
073: FieldContextLastInterpreterFrame_ = Context.class
074: .getDeclaredField("lastInterpreterFrame");
075: FieldContextLastInterpreterFrame_.setAccessible(true);
076: } catch (final Exception e) {
077: throw new Error(
078: "Bad Rhino version: can't install custom String primitive prototype fix");
079: }
080: }
081:
082: /**
083: * Install the workaround for the Rhino bug
084: * @param topScope the top scope (which contains the standard objects)
085: * @throws Exception if initialization fails
086: */
087: static void installWorkaround(final Scriptable topScope)
088: throws Exception {
089: final BaseFunction stringObj = (BaseFunction) topScope.get(
090: "String", topScope);
091: final Scriptable stringObjPrototype = (Scriptable) FieldPrototypeProperty_
092: .get(stringObj);
093: final StringPrimitivePrototypeBugFixer prototypeWrapper = new StringPrimitivePrototypeBugFixer(
094: stringObjPrototype);
095: FieldPrototypeProperty_.set(stringObj, prototypeWrapper);
096: }
097:
098: private final Scriptable wrapped_;
099:
100: StringPrimitivePrototypeBugFixer(final Scriptable wrapped) {
101: wrapped_ = wrapped;
102: }
103:
104: /**
105: * {@inheritDoc}
106: */
107: public void delete(final int index) {
108: wrapped_.delete(index);
109: }
110:
111: /**
112: * {@inheritDoc}
113: */
114: public void delete(final String name) {
115: wrapped_.delete(name);
116: }
117:
118: /**
119: * {@inheritDoc}
120: */
121: public Object get(final int index, Scriptable start) {
122: if (start == this ) {
123: start = wrapped_;
124: }
125: return wrapped_.get(index, start);
126: }
127:
128: private Scriptable getRealScope() throws Exception {
129: final Object o = FieldContextLastInterpreterFrame_.get(Context
130: .getCurrentContext());
131: if (FieldInterpreterCallFrameScope_ == null) {
132: FieldInterpreterCallFrameScope_ = o.getClass()
133: .getDeclaredField("scope");
134: FieldInterpreterCallFrameScope_.setAccessible(true);
135: }
136: return (Scriptable) FieldInterpreterCallFrameScope_.get(o);
137:
138: }
139:
140: /**
141: * {@inheritDoc}
142: */
143: public Object get(final String name, Scriptable start) {
144: try {
145: final Scriptable originalScope = getRealScope();
146: final Scriptable originalTopScope = ScriptableObject
147: .getTopLevelScope(originalScope);
148: final Scriptable currentTopScope = ScriptableObject
149: .getTopLevelScope(this );
150: if (originalTopScope != currentTopScope) {
151: final Scriptable s = (Scriptable) originalTopScope.get(
152: "String", originalTopScope);
153: final Scriptable p = (Scriptable) s.get("prototype", s);
154: return p.get(name, p);
155: }
156: } catch (final Exception e) {
157: throw new RuntimeException(e);
158: }
159:
160: if (start == this ) {
161: start = wrapped_;
162: }
163: return wrapped_.get(name, start);
164: }
165:
166: /**
167: * {@inheritDoc}
168: */
169: public String getClassName() {
170: return wrapped_.getClassName();
171: }
172:
173: /**
174: * {@inheritDoc}
175: */
176: public Object getDefaultValue(final Class hint) {
177: return wrapped_.getDefaultValue(hint);
178: }
179:
180: /**
181: * {@inheritDoc}
182: */
183: public Object[] getIds() {
184: return wrapped_.getIds();
185: }
186:
187: /**
188: * {@inheritDoc}
189: */
190: public Scriptable getParentScope() {
191: return wrapped_.getParentScope();
192: }
193:
194: /**
195: * {@inheritDoc}
196: */
197: public Scriptable getPrototype() {
198: return wrapped_.getPrototype();
199: }
200:
201: /**
202: * {@inheritDoc}
203: */
204: public boolean has(final int index, Scriptable start) {
205: if (start == this ) {
206: start = wrapped_;
207: }
208: return wrapped_.has(index, start);
209: }
210:
211: /**
212: * {@inheritDoc}
213: */
214: public boolean has(final String name, Scriptable start) {
215: if (start == this ) {
216: start = wrapped_;
217: }
218: return wrapped_.has(name, start);
219: }
220:
221: /**
222: * {@inheritDoc}
223: */
224: public boolean hasInstance(final Scriptable instance) {
225: return wrapped_.hasInstance(instance);
226: }
227:
228: /**
229: * {@inheritDoc}
230: */
231: public void put(final int index, Scriptable start,
232: final Object value) {
233: if (start == this ) {
234: start = wrapped_;
235: }
236: wrapped_.put(index, start, value);
237: }
238:
239: /**
240: * {@inheritDoc}
241: */
242: public void put(final String name, Scriptable start,
243: final Object value) {
244: if (start == this ) {
245: start = wrapped_;
246: }
247: wrapped_.put(name, start, value);
248: }
249:
250: /**
251: * {@inheritDoc}
252: */
253: public void setParentScope(final Scriptable parent) {
254: wrapped_.setParentScope(parent);
255: }
256:
257: /**
258: * {@inheritDoc}
259: */
260: public void setPrototype(final Scriptable prototype) {
261: wrapped_.setPrototype(prototype);
262: }
263: }
|