/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.scripting; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.regex.Pattern; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.Function; import org.mozilla.javascript.ImporterTopLevel; import org.mozilla.javascript.JavaScriptException; import org.mozilla.javascript.NativeFunction; import org.mozilla.javascript.NativeJavaClass; import org.mozilla.javascript.NativeJavaObject; import org.mozilla.javascript.ScriptRuntime; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Undefined; import org.mozilla.javascript.Wrapper; import com.servoy.j2db.ApplicationException; import com.servoy.j2db.ExitScriptException; import com.servoy.j2db.FormController; import com.servoy.j2db.FormManager.HistoryProvider; import com.servoy.j2db.IApplication; import com.servoy.j2db.IServiceProvider; import com.servoy.j2db.ISmartClientApplication; import com.servoy.j2db.J2DBGlobals; import com.servoy.j2db.dataprocessing.DataException; import com.servoy.j2db.dataprocessing.FoundSet; import com.servoy.j2db.dataprocessing.IDataServer; import com.servoy.j2db.dataprocessing.JSDataSet; import com.servoy.j2db.dataprocessing.JSDatabaseManager; import com.servoy.j2db.dataprocessing.Record; import com.servoy.j2db.dataprocessing.ValueFactory.DbIdentValue; import com.servoy.j2db.dataprocessing.datasource.JSDataSources; import com.servoy.j2db.documentation.ServoyDocumented; import com.servoy.j2db.persistence.Form; import com.servoy.j2db.persistence.IRepository; import com.servoy.j2db.persistence.IScriptProvider; import com.servoy.j2db.persistence.ISupportScriptProviders; import com.servoy.j2db.persistence.ITable; import com.servoy.j2db.persistence.RepositoryException; import com.servoy.j2db.persistence.ScriptCalculation; import com.servoy.j2db.persistence.ScriptMethod; import com.servoy.j2db.persistence.ScriptVariable; import com.servoy.j2db.persistence.Solution; import com.servoy.j2db.persistence.TableNode; import com.servoy.j2db.scripting.solutionmodel.JSField; import com.servoy.j2db.scripting.solutionmodel.JSFieldWithConstants; import com.servoy.j2db.scripting.solutionmodel.JSMethodWithArguments; import com.servoy.j2db.scripting.solutionmodel.JSSolutionModel; import com.servoy.j2db.server.shared.IPerfomanceRegistry; import com.servoy.j2db.ui.DataRendererOnRenderWrapper; import com.servoy.j2db.ui.IScriptAccordionPanelMethods; import com.servoy.j2db.ui.IScriptDataLabelMethods; import com.servoy.j2db.ui.IScriptPortalComponentMethods; import com.servoy.j2db.ui.IScriptScriptLabelMethods; import com.servoy.j2db.ui.IScriptSplitPaneMethods; import com.servoy.j2db.ui.IScriptTabPanelMethods; import com.servoy.j2db.ui.RenderableWrapper; import com.servoy.j2db.ui.runtime.IRuntimeButton; import com.servoy.j2db.ui.runtime.IRuntimeCalendar; import com.servoy.j2db.ui.runtime.IRuntimeCheck; import com.servoy.j2db.ui.runtime.IRuntimeChecks; import com.servoy.j2db.ui.runtime.IRuntimeCombobox; import com.servoy.j2db.ui.runtime.IRuntimeComponent; import com.servoy.j2db.ui.runtime.IRuntimeDataButton; import com.servoy.j2db.ui.runtime.IRuntimeHtmlArea; import com.servoy.j2db.ui.runtime.IRuntimeImageMedia; import com.servoy.j2db.ui.runtime.IRuntimeListBox; import com.servoy.j2db.ui.runtime.IRuntimePassword; import com.servoy.j2db.ui.runtime.IRuntimeRadio; import com.servoy.j2db.ui.runtime.IRuntimeRadios; import com.servoy.j2db.ui.runtime.IRuntimeRtfArea; import com.servoy.j2db.ui.runtime.IRuntimeSpinner; import com.servoy.j2db.ui.runtime.IRuntimeTextArea; import com.servoy.j2db.ui.runtime.IRuntimeTextField; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.ServoyException; import com.servoy.j2db.util.UUID; import com.servoy.j2db.util.Utils; /** * Instances of this clas should only exist as long as the duration of a solution! * * @author jblok, jcompagner */ public class ScriptEngine implements IScriptSupport { public static final String SERVOY_DISABLE_SCRIPT_COMPILE_PROPERTY = "servoy.disableScriptCompile"; //$NON-NLS-1$ private final static Pattern docStripper = Pattern.compile( "\\A\\s*(?:/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/)?\\s*function\\s*(?:/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/)*\\s*([\\w\\$]+)"); //$NON-NLS-1$ private final static ContextFactory.Listener contextListener = new ContextFactory.Listener() { public void contextCreated(Context cx) { IServiceProvider sp = J2DBGlobals.getServiceProvider(); if (sp instanceof IApplication) { IApplication application = (IApplication)sp; cx.setApplicationClassLoader(application.getBeanManager().getClassLoader()); cx.setWrapFactory(new ServoyWrapFactory(application)); } } public void contextReleased(Context cx) { cx.setApplicationClassLoader(null); } }; static { class ServoyContextFactory extends ContextFactory { @Override protected boolean hasFeature(Context context, int featureIndex) { if (featureIndex == Context.FEATURE_LOCATION_INFORMATION_IN_ERROR) return true; return super.hasFeature(context, featureIndex); } } ContextFactory.initGlobal(new ServoyContextFactory()); ContextFactory.getGlobal().addListener(contextListener); } protected IApplication application; private HashMap<ITable, Scriptable> tableScopes; //scopes protected ServoyTopLevelScope toplevelScope; protected SolutionScope solutionScope; protected ScopesScope scopesScope; private PluginScope pluginScope; private CreationalPrototype creator; private final JSApplication jsApplication; private final JSUtils jsUtils; private final JSSecurity jssec; private final JSDatabaseManager jsdbm; private final JSDataSources jsds; private final JSI18N i18n; private final HistoryProvider historyProvider; private final JSSolutionModel solutionModifier; @SuppressWarnings("nls") public ScriptEngine(IApplication app) { application = app; jsApplication = new JSApplication(application); jsUtils = new JSUtils(application); jssec = new JSSecurity(application); jsdbm = new JSDatabaseManager(application); jsds = new JSDataSources(application); i18n = new JSI18N(application); historyProvider = new HistoryProvider(application); solutionModifier = createSolutionModifier(); Context cx = Context.enter(); try { toplevelScope = new ServoyTopLevelScope(cx); ScriptObjectRegistry.getJavaMembers(UUID.class, toplevelScope); ScriptObjectRegistry.getJavaMembers(JSMethodWithArguments.class, toplevelScope); ScriptObjectRegistry.getJavaMembers(DbIdentValue.class, toplevelScope); // Note: for tracking used values in calculations, we use RecordingScriptable to wrap scriptables. // We do not wrap toplevelscope because rhino expects the top level scope to extend TopLevel class. // This means that everything stored in the toplevelScope directly will not be tracked when used by calculations. toplevelScope.put(Record.JS_RECORD, toplevelScope, new InstanceOfScope(Record.JS_RECORD, Record.class)); toplevelScope.put(FoundSet.JS_FOUNDSET, toplevelScope, new InstanceOfScope(FoundSet.JS_FOUNDSET, FoundSet.class)); toplevelScope.put("JSFoundset", toplevelScope, new InstanceOfScope("JSFoundset", FoundSet.class)); toplevelScope.put("JSDataSet", toplevelScope, new InstanceOfScope("JSDataSet", JSDataSet.class)); toplevelScope.put("Form", toplevelScope, new InstanceOfScope("Form", FormScope.class)); toplevelScope.put("RuntimeForm", toplevelScope, new InstanceOfScope("RuntimeForm", FormScope.class)); toplevelScope.put("RuntimeComponent", toplevelScope, new InstanceOfScope("RuntimeComponent", IRuntimeComponent.class)); toplevelScope.put("RuntimeButton", toplevelScope, new InstanceOfScope("RuntimeButton", IRuntimeButton.class)); toplevelScope.put("RuntimeDataButton", toplevelScope, new InstanceOfScope("RuntimeDataButton", IRuntimeDataButton.class)); toplevelScope.put("RuntimeLabel", toplevelScope, new InstanceOfScope("RuntimeLabel", IScriptScriptLabelMethods.class)); toplevelScope.put("RuntimeDataLabel", toplevelScope, new InstanceOfScope("RuntimeDataLabel", IScriptDataLabelMethods.class)); toplevelScope.put("RuntimePassword", toplevelScope, new InstanceOfScope("RuntimePassword", IRuntimePassword.class)); toplevelScope.put("RuntimeHtmlArea", toplevelScope, new InstanceOfScope("RuntimeHtmlArea", IRuntimeHtmlArea.class)); toplevelScope.put("RuntimeRtfArea", toplevelScope, new InstanceOfScope("RuntimeRtfArea", IRuntimeRtfArea.class)); toplevelScope.put("RuntimeTextArea", toplevelScope, new InstanceOfScope("RuntimeTextArea", IRuntimeTextArea.class)); toplevelScope.put("RuntimeChecks", toplevelScope, new InstanceOfScope("RuntimeChecks", IRuntimeChecks.class)); toplevelScope.put("RuntimeCheck", toplevelScope, new InstanceOfScope("RuntimeCheck", IRuntimeCheck.class)); toplevelScope.put("RuntimeRadios", toplevelScope, new InstanceOfScope("RuntimeRadios", IRuntimeRadios.class)); toplevelScope.put("RuntimeRadio", toplevelScope, new InstanceOfScope("RuntimeRadio", IRuntimeRadio.class)); toplevelScope.put("RuntimeComboBox", toplevelScope, new InstanceOfScope("RuntimeComboBox", IRuntimeCombobox.class)); toplevelScope.put("RuntimeCalendar", toplevelScope, new InstanceOfScope("RuntimeCalendar", IRuntimeCalendar.class)); toplevelScope.put("RuntimeMediaField", toplevelScope, new InstanceOfScope("RuntimeMediaField", IRuntimeImageMedia.class)); toplevelScope.put("RuntimeTextField", toplevelScope, new InstanceOfScope("RuntimeTextField", IRuntimeTextField.class)); toplevelScope.put("RuntimeTabPanel", toplevelScope, new InstanceOfScope("RuntimeTabPanel", IScriptTabPanelMethods.class)); toplevelScope.put("RuntimeSplitPane", toplevelScope, new InstanceOfScope("RuntimeSplitPane", IScriptSplitPaneMethods.class)); toplevelScope.put("RuntimePortal", toplevelScope, new InstanceOfScope("RuntimePortal", IScriptPortalComponentMethods.class)); toplevelScope.put("RuntimeListBox", toplevelScope, new InstanceOfScope("RuntimeListBox", IRuntimeListBox.class)); toplevelScope.put("RuntimeSpinner", toplevelScope, new InstanceOfScope("RuntimeSpinner", IRuntimeSpinner.class)); toplevelScope.put("RuntimeAccordionPanel", toplevelScope, new InstanceOfScope("RuntimeAccordionPanel", IScriptAccordionPanelMethods.class)); toplevelScope.put("DataException", toplevelScope, new InstanceOfScope("DataException", DataException.class)); ScriptObjectRegistry.getJavaMembers(FormController.JSForm.class, toplevelScope); toplevelScope.setPrototype(null); SolutionScope tmpSolutionScope = new SolutionScope(toplevelScope); InstanceJavaMembers ijm = new InstanceJavaMembers(toplevelScope, historyProvider.getClass()); Scriptable history = new NativeJavaObject(tmpSolutionScope, historyProvider, ijm); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_HISTORY, tmpSolutionScope, history); pluginScope = new PluginScope(tmpSolutionScope, application); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_PLUGINS, tmpSolutionScope, pluginScope); // add application variable to solution scope tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_APPLICATION, tmpSolutionScope, new NativeJavaObject(tmpSolutionScope, jsApplication, new InstanceJavaMembers(tmpSolutionScope, JSApplication.class))); registerScriptObjectReturnTypes(jsApplication); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_UTILS, tmpSolutionScope, new NativeJavaObject(tmpSolutionScope, jsUtils, new InstanceJavaMembers(tmpSolutionScope, JSUtils.class))); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_SECURITY, tmpSolutionScope, new NativeJavaObject(tmpSolutionScope, jssec, new InstanceJavaMembers(tmpSolutionScope, JSSecurity.class))); registerScriptObjectReturnTypes(jssec); tmpSolutionScope.put(JSSecurity.class.getSimpleName(), tmpSolutionScope, new NativeJavaClass(tmpSolutionScope, JSSecurity.class)); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_SOLUTION_MODIFIER, tmpSolutionScope, new NativeJavaObject(tmpSolutionScope, solutionModifier, new InstanceJavaMembers(tmpSolutionScope, JSSolutionModel.class))); registerScriptObjectClass(JSSolutionModel.class); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_DATABASE_MANAGER, tmpSolutionScope, new NativeJavaObject(tmpSolutionScope, jsdbm, new InstanceJavaMembers(tmpSolutionScope, JSDatabaseManager.class))); registerScriptObjectClass(JSDatabaseManager.class); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_DATASOURCES, tmpSolutionScope, new NativeJavaObject(tmpSolutionScope, jsds, new InstanceJavaMembers(tmpSolutionScope, JSDataSources.class))); registerScriptObjectClass(JSDataSources.class); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_I18N, tmpSolutionScope, new NativeJavaObject(tmpSolutionScope, i18n, new InstanceJavaMembers(tmpSolutionScope, JSI18N.class))); ScriptObjectRegistry.getJavaMembers(RepositoryException.class, tmpSolutionScope); ScriptObjectRegistry.getJavaMembers(ApplicationException.class, tmpSolutionScope); ScriptObjectRegistry.getJavaMembers(ServoyException.class, tmpSolutionScope); ScriptObjectRegistry.getJavaMembers(DataException.class, tmpSolutionScope); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_SERVOY_EXCEPTION, tmpSolutionScope, new NativeJavaClass(tmpSolutionScope, ServoyException.class)); registerScriptObjectClass(ServoyException.class); ScriptObjectRegistry.getJavaMembers(DataRendererOnRenderWrapper.class, tmpSolutionScope); ScriptObjectRegistry.getJavaMembers(RenderableWrapper.class, tmpSolutionScope); creator = new CreationalPrototype(tmpSolutionScope, application); creator.setPrototype(null); tmpSolutionScope.put(IExecutingEnviroment.TOPLEVEL_FORMS, tmpSolutionScope, creator); scopesScope = new ScopesScope(tmpSolutionScope, this, app); tmpSolutionScope.setScopesScope(scopesScope); solutionScope = tmpSolutionScope; toplevelScope.setSealReadOnly(true); } catch (Exception ex) { Debug.error("ScriptEngine init not completly successfully ", ex); //$NON-NLS-1$ } finally { Context.exit(); } } protected JSSolutionModel createSolutionModifier() { return new JSSolutionModel(application); } @SuppressWarnings("deprecation") private void registerScriptObjectClass(Class< ? > cls) { IScriptObject scriptObjectForClass = ScriptObjectRegistry.getScriptObjectForClass(cls); application.updateUI(1); // helps SC not freeze and it should not sleep at all, only process UI events if available for max 1 ms if (scriptObjectForClass != null) { registerScriptObjectReturnTypes(scriptObjectForClass); } else { if (IReturnedTypesProvider.class.isAssignableFrom(cls)) { try { registerScriptObjectReturnTypes((IReturnedTypesProvider)cls.newInstance()); } catch (Exception e) { // ignore } } } } /** * @param scriptObject * @throws InstantiationException * @throws IllegalAccessException */ public void registerScriptObjectReturnTypes(IReturnedTypesProvider scriptObject, IScriptableAddition scriptableAddition) { if (scriptObject == null) return; Class< ? >[] allReturnedTypes = scriptObject.getAllReturnedTypes(); if (allReturnedTypes == null) return; try { toplevelScope.setSealReadOnly(false); for (Class< ? > element : allReturnedTypes) { if (!(Scriptable.class.isAssignableFrom(element))) { ScriptObjectRegistry.getJavaMembers(element, toplevelScope); } if (IPrefixedConstantsObject.class.isAssignableFrom(element)) { try { IPrefixedConstantsObject constants = (IPrefixedConstantsObject)element.newInstance(); toplevelScope.put(constants.getPrefix(), toplevelScope, new NativeJavaClass(toplevelScope, element.equals(JSFieldWithConstants.class) ? JSField.class : element)); if (scriptableAddition != null) { scriptableAddition.addVar(constants.getPrefix(), new NativeJavaClass(scriptableAddition, element)); } } catch (Exception e) { Debug.error(e); } } else if (IJavaScriptType.class.isAssignableFrom(element)) // constants or javascript types { try { String name = element.getSimpleName(); ServoyDocumented sd = element.getAnnotation(ServoyDocumented.class); if (sd != null && sd.scriptingName() != null && sd.scriptingName().trim().length() > 0) { // documentation has overridden scripting name name = sd.scriptingName().trim(); } toplevelScope.put(name, toplevelScope, new NativeJavaClass(toplevelScope, element)); if (scriptableAddition != null) { scriptableAddition.addVar(name, new NativeJavaClass(scriptableAddition, element)); } } catch (Exception e) { Debug.error(e); } } } } finally { toplevelScope.setSealReadOnly(true); } } public void registerScriptObjectReturnTypes(IReturnedTypesProvider scriptObject) { registerScriptObjectReturnTypes(scriptObject, null); } public void destroy() { ScriptObjectRegistry.removeEntryFromCache(toplevelScope); jsApplication.destroy(); scopesScope.destroy(); jsdbm.destroy(); jsds.destroy(); jssec.destroy(); jsUtils.destroy(); i18n.destroy(); historyProvider.destroy(); pluginScope.destroy(); creator.destroy(); solutionModifier.destroy(); } public Object getSystemConstant(String name) { if ("serverURL".equals(name)) //$NON-NLS-1$ { return application.getServerURL().toString(); } return null; } public ScopesScope getScopesScope() { return scopesScope; } /** * @return the toplevelScope */ public Scriptable getToplevelScope() { return toplevelScope; } public SolutionScope getSolutionScope() { return solutionScope; } /** * @return the solutionModifier */ public JSSolutionModel getSolutionModifier() { return solutionModifier; } protected final Scriptable getExistingTableScrope(ITable table) { if (tableScopes != null) { return tableScopes.get(table); } return null; } public Scriptable getTableScope(final ITable table) { if (tableScopes == null) tableScopes = new HashMap<ITable, Scriptable>(); Scriptable tableScope = null; synchronized (table) { tableScope = tableScopes.get(table); if (tableScope == null) { Context.enter(); try { tableScope = new TableScope(solutionScope, this, table, application.getFlattenedSolution(), new ISupportScriptProviders() { public Iterator< ? extends IScriptProvider> getScriptMethods(boolean sort) { return application.getFlattenedSolution().getScriptCalculations(table, false); } public Iterator<ScriptVariable> getScriptVariables(boolean b) { return Collections.<ScriptVariable> emptyList().iterator(); } public ScriptMethod getScriptMethod(int methodId) { return null; // is not used for calculations } }); tableScopes.put(table, tableScope); } finally { Context.exit(); } } } return tableScope; } @SuppressWarnings("nls") public Function compileFunction(IScriptProvider sp, Scriptable scope) throws Exception { Context cx = Context.enter(); int iOp = cx.getOptimizationLevel(); String sourceName = sp.getDataProviderID(); if (sp.getScopeName() != null) { Solution sol = (Solution)sp.getAncestor(IRepository.SOLUTIONS); sourceName = sol.getName() + "/scopes/" + sp.getScopeName() + '/' + sourceName; } else if (sp.getParent() instanceof Form) { Solution sol = (Solution)sp.getAncestor(IRepository.SOLUTIONS); sourceName = sol.getName() + "/forms/" + ((Form)sp.getParent()).getName() + '/' + sourceName; } else if (sp.getParent() instanceof TableNode) { Solution sol = (Solution)sp.getAncestor(IRepository.SOLUTIONS); sourceName = sol.getName() + '/' + ((TableNode)sp.getParent()).getDataSource() + '/' + sourceName; if (sp instanceof ScriptCalculation) { sourceName = sourceName.replace("db:", "calculations"); } else { sourceName = sourceName.replace("db:", "entities"); } } try { if (Utils.getAsBoolean(System.getProperty(SERVOY_DISABLE_SCRIPT_COMPILE_PROPERTY, "false"))) //flag should only be used in rich client { cx.setOptimizationLevel(-1); } else { cx.setOptimizationLevel(9); } cx.setGeneratingSource(Boolean.getBoolean("servoy.generateJavascriptSource")); return compileScriptProvider(sp, scope, cx, sourceName); } catch (Exception e) { Debug.error("Compilation failed for method: " + sourceName); throw e; } finally { cx.setOptimizationLevel(iOp); Context.exit(); } } /** * @param sp * @param scope * @param cx * @return */ @SuppressWarnings("nls") protected Function compileScriptProvider(IScriptProvider sp, Scriptable scope, Context cx, String sourceName) { String declaration = sp.getDeclaration(); // for script calcs we change the name a bit so that references to itself // dont return the calc function itself but still the value. if (sp instanceof ScriptCalculation) { declaration = extractFunction(declaration, "function $1_"); } else { declaration = extractFunction(declaration, "function $1"); } Function f = cx.compileFunction(scope, declaration, sourceName, sp.getLineNumberOffset(), null); if (!(sp instanceof ScriptCalculation)) { if (sp.getScopeName() != null) { f.put("_scopename_", f, sp.getScopeName()); //$NON-NLS-1$ } f.put("_methodname_", f, sp.getDataProviderID()); //$NON-NLS-1$ f.put("_AllowToRunInFind_", f, Boolean.valueOf(sp.getDeclaration().indexOf("@AllowToRunInFind") != -1 || declaration.indexOf(".search") != -1 || //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ declaration.indexOf("controller.loadAllRecords") != -1)); } return f; } public Object executeFunction(Function f, Scriptable scope, Scriptable thisObject, Object[] args, boolean focusEvent, boolean throwException) throws Exception { if (!application.isSolutionLoaded()) { return null; } //find function Object retValue = null; if (f != null) { String userUidBefore = null; if (Context.getCurrentContext() == null) { userUidBefore = application.getClientInfo().getUserUid(); } Context cx = Context.enter(); // only search for nice strings needed in performance admin page if performance is actually enabled IPerfomanceRegistry performanceRegistry = (application.getApplicationServerAccess() != null ? application.getApplicationServerAccess().getFunctionPerfomanceRegistry() : null); String methodName = null; String solutionName = null; if (performanceRegistry != null && performanceRegistry.isEnabled()) { methodName = f.getClassName(); if (f instanceof NativeFunction) methodName = ((NativeFunction)f).getFunctionName(); String scopeName = scope.getClassName(); if (scope instanceof LazyCompilationScope) scopeName = ((LazyCompilationScope)scope).getScopeName(); if (scope instanceof FoundSet) { Scriptable parentScope = ((FoundSet)scope).getPrototype(); if (parentScope instanceof LazyCompilationScope) { scopeName = ((LazyCompilationScope)parentScope).getScopeName(); } } solutionName = application.getSolutionName(); methodName = scopeName + "." + methodName; //$NON-NLS-1$ } UUID pfUuid = null; try { if (application instanceof ISmartClientApplication) { ((ISmartClientApplication)application).setPaintTableImmediately(false); } // wrap objects when needed (to support typeof(arguments[0]) ) Object[] wrappedArgs; if (args == null) { wrappedArgs = new Object[0]; } else { wrappedArgs = new Object[args.length]; for (int i = 0; i < args.length; i++) { if (args[i] != null) { wrappedArgs[i] = cx.getWrapFactory().wrap(cx, scope, args[i], args[i].getClass()); } } } //run if (performanceRegistry != null && performanceRegistry.isEnabled() && !(application instanceof ISmartClientApplication)) { // application.addPerformanceTiming(server, sql, 0 - t1); pfUuid = performanceRegistry.getPerformanceData(solutionName).startAction(methodName, System.currentTimeMillis(), IDataServer.METHOD_CALL, application.getClientID()); } retValue = f.call(cx, scope, thisObject, wrappedArgs); if (retValue instanceof Wrapper) { retValue = ((Wrapper)retValue).unwrap(); } } catch (Exception ex) { if (scope instanceof TableScope || throwException) { throw ex;//is calc report back via other way } else if (application.getSolution() != null) { application.handleException(null, ex); } else { Debug.trace("Solution already closed, ignoring exception", ex); //$NON-NLS-1$ } } finally { if (application instanceof ISmartClientApplication) { ((ISmartClientApplication)application).setPaintTableImmediately(true); } else if (pfUuid != null) { performanceRegistry.getPerformanceData(solutionName).endAction(pfUuid); } Context.exit(); } testClientUidChange(scope, userUidBefore); } return retValue; } /** * @param scope * @param userUidBefore */ private void testClientUidChange(Scriptable scope, String userUidBefore) { if (Context.getCurrentContext() == null && !application.isShutDown()) { if (application.isEventDispatchThread() && !(scope instanceof TableScope)) { application.getFlattenedSolution().checkStateForms(application); } String userUidAfter = application.getClientInfo().getUserUid(); if (!Utils.stringSafeEquals(userUidBefore, userUidAfter)) { // user was authenticated or logged out // Use invokeLater because the event that triggered this method may be followed by using code that still needs to run with the old solution (SVY-3335). final String uidBefore = userUidBefore; final String uidAfter = userUidAfter; application.invokeLater(new Runnable() { public void run() { application.handleClientUserUidChanged(uidBefore, uidAfter); } }); } } } public Object eval(Scriptable scope, String eval_string) { String userUidBefore = null; if (Context.getCurrentContext() == null) { userUidBefore = application.getClientInfo().getUserUid(); } Context cx = Context.enter(); try { Object o = null; Function compileFunction = cx.compileFunction(scope, "function evalFunction(){}", "evalFunction", 0, null); //$NON-NLS-1$ //$NON-NLS-2$ if (compileFunction instanceof NativeFunction) { o = cx.evaluateString(ScriptRuntime.createFunctionActivation((NativeFunction)compileFunction, scope, null), eval_string, "internal_anon", 1, //$NON-NLS-1$ null); } else { o = cx.evaluateString(scope, eval_string, "internal_anon", 1, null); //$NON-NLS-1$ } if (o instanceof Wrapper) { o = ((Wrapper)o).unwrap(); } if (o == Scriptable.NOT_FOUND || o == Undefined.instance) { o = null; } return o; } catch (Exception ex) { if (ex instanceof ExitScriptException) { throw (ExitScriptException)ex; } else if (ex.getCause() instanceof ExitScriptException) { throw (ExitScriptException)ex.getCause(); } else if (ex instanceof JavaScriptException && ((JavaScriptException)ex).getValue() instanceof ExitScriptException) { throw (ExitScriptException)((JavaScriptException)ex).getValue(); } else if (application.getSolution() != null) { application.reportError(application.getI18NMessage("servoy.formPanel.error.evalString") + eval_string + "'", ex); //$NON-NLS-1$//$NON-NLS-2$ } else { Debug.trace("Solution already closed, ignoring exception", ex); //$NON-NLS-1$ } } finally { Context.exit(); testClientUidChange(scope, userUidBefore); } return null; } public void flushCachedScopes() { reload(); } public void setLastKeyModifiers(int m) { getJSApplication().setLastKeyModifiers(m); } /** * @see com.servoy.j2db.scripting.IExecutingEnviroment#reload() */ public void reload() { scopesScope = new ScopesScope(solutionScope, this, application); solutionScope.setScopesScope(scopesScope); scopesScope.createGlobalsScope(); } public boolean isAWTSuspendedRunningScript() { return false; } public JSApplication getJSApplication() { return jsApplication; } /** * @author jcompagner * */ private final class ServoyTopLevelScope extends ImporterTopLevel { private final Set<String> readonlyProperties = new HashSet<String>(); // if false then the properties are recorded as readonly // if true then the properties are checked as readonly // default it starts in recording mode private boolean sealedReadOnly = false; /** * @param cx */ private ServoyTopLevelScope(Context cx) { super(cx); } public void setSealReadOnly(boolean sealed) { this.sealedReadOnly = sealed; } @SuppressWarnings("nls") @Override public synchronized void put(String name, Scriptable start, Object value) { // must test for null, because put is already called by the super class. if (readonlyProperties != null) { if (!sealedReadOnly) { readonlyProperties.add(name); } else if (readonlyProperties.contains(name)) { throw new RuntimeException("Property " + name + " is readonly"); } } super.put(name, start, value); } @Override public synchronized void put(int index, Scriptable start, Object value) { super.put(index, start, value); } @Override public synchronized Object get(int index, Scriptable start) { return super.get(index, start); } @Override public synchronized Object get(String name, Scriptable start) { return super.get(name, start); } } public static String extractFunction(String declaration, String replacement) { int docIndex = declaration.indexOf("/**"); int functionKeywordIndex = declaration.indexOf("function "); if (docIndex > 0 && docIndex < functionKeywordIndex) { declaration = declaration.substring(docIndex); } return docStripper.matcher(declaration).replaceFirst(replacement); } }