/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Norris Boyd * Igor Bukanov * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package org.mozilla.javascript; import java.lang.reflect.*; import java.security.AccessController; import java.security.PrivilegedAction; /** * Avoid loading classes unless they are used. * * <p> This improves startup time and average memory usage. */ public final class LazilyLoadedCtor implements java.io.Serializable { private static final long serialVersionUID = 1L; private static final int STATE_BEFORE_INIT = 0; private static final int STATE_INITIALIZING = 1; private static final int STATE_WITH_VALUE = 2; private final ScriptableObject scope; private final String propertyName; private final String className; private final boolean sealed; private final boolean privileged; private Object initializedValue; private int state; public LazilyLoadedCtor(ScriptableObject scope, String propertyName, String className, boolean sealed) { this(scope, propertyName, className, sealed, false); } LazilyLoadedCtor(ScriptableObject scope, String propertyName, String className, boolean sealed, boolean privileged) { this.scope = scope; this.propertyName = propertyName; this.className = className; this.sealed = sealed; this.privileged = privileged; this.state = STATE_BEFORE_INIT; scope.addLazilyInitializedValue(propertyName, 0, this, ScriptableObject.DONTENUM); } void init() { synchronized (this) { if (state == STATE_INITIALIZING) throw new IllegalStateException( "Recursive initialization for "+propertyName); if (state == STATE_BEFORE_INIT) { state = STATE_INITIALIZING; // Set value now to have something to set in finally block if // buildValue throws. Object value = Scriptable.NOT_FOUND; try { value = buildValue(); } finally { initializedValue = value; state = STATE_WITH_VALUE; } } } } Object getValue() { if (state != STATE_WITH_VALUE) throw new IllegalStateException(propertyName); return initializedValue; } private Object buildValue() { if(privileged) { return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return buildValue0(); } }); } else { return buildValue0(); } } private Object buildValue0() { Class<? extends Scriptable> cl = cast(Kit.classOrNull(className)); if (cl != null) { try { Object value = ScriptableObject.buildClassCtor(scope, cl, sealed, false); if (value != null) { return value; } else { // cl has own static initializer which is expected // to set the property on its own. value = scope.get(propertyName, scope); if (value != Scriptable.NOT_FOUND) return value; } } catch (InvocationTargetException ex) { Throwable target = ex.getTargetException(); if (target instanceof RuntimeException) { throw (RuntimeException)target; } } catch (RhinoException ex) { } catch (InstantiationException ex) { } catch (IllegalAccessException ex) { } catch (SecurityException ex) { } } return Scriptable.NOT_FOUND; } @SuppressWarnings({"unchecked"}) private Class<? extends Scriptable> cast(Class<?> cl) { return (Class<? extends Scriptable>)cl; } }