/* * JSwiff is an open source Java API for Macromedia Flash file generation * and manipulation * * Copyright (C) 2004-2005 Ralf Terdic (contact@jswiff.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.jswiff.swfrecords.actions; import com.jswiff.io.InputBitStream; import com.jswiff.io.OutputBitStream; import com.jswiff.swfrecords.RegisterParam; import java.io.IOException; import java.io.UnsupportedEncodingException; /** * This action is used to declare a function. It supersedes * <code>DefineFunction</code> since SWF 7.<br> * With DefineFunction2, a function may allocate its own private set of up to * 256 registers, which can be used as parameters or local variables.<br> * For performance improvement, you can specify if "common variables" * (<code>_parent, _root, super, arguments, this,</code> or * <code>_global</code>) are supposed to be preloaded into registers before * execution. Additionally, the Flash Player can be instructed to suppress * unused variables. (Only <code>super, arguments</code> and <code>this</code> * can be suppressed. Naturally, you can either preload or suppress a * variable, not both). * * <p> * Performed stack operations: * * <ul> * <li> * standard function declarations do not touch the stack * </li> * <li> * when a function name is not specified, it is assumed that a function literal * (anonymous function) is declared. In this case, the declared function is * pushed to the stack so it can either be assigned or invoked:<br> * </li> * </ul> * </p> * * <p> * Note: Use <code>Return</code> to declare the function's result. Otherwise * the function has no result, and <code>undefined</code> is pushed to stack * upon invocation. * </p> * * <p> * ActionScript equivalents: * * <ul> * <li> * standard function declaration, e.g.<br> * <code>myFunction(x) {<br> * return (x + 3);<br>}</code><br> * </li> * <li> * anonymous function declaration, e.g.<br> * <code>function (x) { x + 3 };</code><br> * </li> * <li> * anonymous function invocation, e.g.<br> * <code>function (x) { x + 3 } (1);</code><br> * </li> * <li> * method declaration * </li> * </ul> * </p> * * @see DefineFunction * @since SWF 7 */ public final class DefineFunction2 extends Action { private String name; private short registerCount; private boolean preloadParent; private boolean preloadRoot; private boolean suppressSuper; private boolean preloadSuper; private boolean suppressArguments; private boolean preloadArguments; private boolean suppressThis; private boolean preloadThis; private boolean preloadGlobal; private RegisterParam[] parameters; private ActionBlockReader body; /** * Creates a new DefineFunction2 action. Use the empty string ("") as * function name for anonymous functions. * * @param name name of the function * @param registerCount number of used registers * @param parameters the function's parameters */ public DefineFunction2( String name, short registerCount, RegisterParam[] parameters) { code = ActionConstants.DEFINE_FUNCTION_2; this.name = name; this.registerCount = registerCount; this.parameters = parameters; body = ActionBlock.getInstance(); } /* * Creates a new DefineFunction2 action. Data is read from a bit stream. * * @param stream a bit stream * * @throws IOException if an I/O error has occured */ DefineFunction2(InputBitStream stream, InputBitStream mainStream) throws IOException { code = ActionConstants.DEFINE_FUNCTION_2; name = stream.readString(); int numParams = stream.readUI16(); registerCount = stream.readUI8(); preloadParent = stream.readBooleanBit(); preloadRoot = stream.readBooleanBit(); suppressSuper = stream.readBooleanBit(); preloadSuper = stream.readBooleanBit(); suppressArguments = stream.readBooleanBit(); preloadArguments = stream.readBooleanBit(); suppressThis = stream.readBooleanBit(); preloadThis = stream.readBooleanBit(); preloadGlobal = ((stream.readUI8() & 1) != 0); // 7 reserved bits=0 parameters = new RegisterParam[numParams]; for (int i = 0; i < numParams; i++) { parameters[i] = new RegisterParam(stream); } int codeSize = stream.readUI16(); // now read further actions from the main stream // read action block byte[] blockBuffer = mainStream.readBytes(codeSize); InputBitStream blockStream = new InputBitStream(blockBuffer); blockStream.setANSI(mainStream.isANSI()); blockStream.setShiftJIS(mainStream.isShiftJIS()); // body = new ActionBlock(blockStream); body = ActionBlock.getInstance(); body.read(blockStream); } /** * Returns an <code>ActionBlock</code> containing the function's body. Use * <code>addAction()</code> to add actions to the body. * * @return function body as action block */ public ActionBlockReader getBody() { return body; } /** * Returns the name of the function (or the empty string for anonymous * functions). * * @return function name */ public String getName() { return name; } /** * Returns an array containing the function's parameters. * * @return the function's parameters */ public RegisterParam[] getParameters() { return parameters; } /** * Returns the number of registers this function uses (max. 256) * * @return number of used registers */ public short getRegisterCount() { return registerCount; } /** * Returns the size of this action record in bytes. * * @return size of this record * * @see Action#getSize() */ public int getSize() { int size = 11 + body.getSize(); try { size += name.getBytes("UTF-8").length; // function name (Unicode, not null-terminated) } catch (UnsupportedEncodingException e) { // UTF-8 should be available } if (parameters != null) { for (int i = 0; i < parameters.length; i++) { size += parameters[i].getSize(); } } return size; } /** * Adds an action record to the function body. * * @param action action record */ public void addAction(Action action) { body.addAction(action); } /** * <p> * Instructs the Flash Player to preload the <code>arguments</code> variable * to a register. * </p> * * <p> * Note: the preloaded variables are copied into registers starting at 1 in * the following order: <code>this</code>, <code>arguments</code>, * <code>super</code>, <code>_root</code>, <code>_parent</code>, and * <code>_global</code>, skipping any that are not to be preloaded. * </p> */ public void preloadArguments() { preloadArguments = true; } /** * <p> * Instructs the Flash Player to preload the <code>_global</code> variable to * a register. * </p> * * <p> * Note: the preloaded variables are copied into registers starting at 1 in * the following order: <code>this</code>, <code>arguments</code>, * <code>super</code>, <code>_root</code>, <code>_parent</code>, and * <code>_global</code>, skipping any that are not to be preloaded. * </p> */ public void preloadGlobal() { preloadGlobal = true; } /** * <p> * Instructs the Flash Player to preload the <code>_parent</code> variable to * a register. * </p> * * <p> * Note: the preloaded variables are copied into registers starting at 1 in * the following order: <code>this</code>, <code>arguments</code>, * <code>super</code>, <code>_root</code>, <code>_parent</code>, and * <code>_global</code>, skipping any that are not to be preloaded. * </p> */ public void preloadParent() { preloadParent = true; } /** * <p> * Instructs the Flash Player to preload the <code>_root</code> variable to a * register. * </p> * * <p> * Note: the preloaded variables are copied into registers starting at 1 in * the following order: <code>this</code>, <code>arguments</code>, * <code>super</code>, <code>_root</code>, <code>_parent</code>, and * <code>_global</code>, skipping any that are not to be preloaded. * </p> */ public void preloadRoot() { preloadRoot = true; } /** * <p> * Instructs the Flash Player to preload the <code>super</code> variable to a * register. * </p> * * <p> * Note: the preloaded variables are copied into registers starting at 1 in * the following order: <code>this</code>, <code>arguments</code>, * <code>super</code>, <code>_root</code>, <code>_parent</code>, and * <code>_global</code>, skipping any that are not to be preloaded. * </p> */ public void preloadSuper() { preloadSuper = true; } /** * <p> * Instructs the Flash Player to preload the <code>this</code> variable to a * register. * </p> * * <p> * Note: the preloaded variables are copied into registers starting at 1 in * the following order: <code>this</code>, <code>arguments</code>, * <code>super</code>, <code>_root</code>, <code>_parent</code>, and * <code>_global</code>, skipping any that are not to be preloaded. * </p> */ public void preloadThis() { preloadThis = true; } /** * Checks whether the <code>arguments</code> variable is preloaded before * executing the function. * * @return <code>true</code> if preloaded, else <code>false</code> */ public boolean preloadsArguments() { return preloadArguments; } /** * Checks whether the <code>_global</code> variable is preloaded before * executing the function. * * @return <code>true</code> if preloaded, else <code>false</code> */ public boolean preloadsGlobal() { return preloadGlobal; } /** * Checks whether the <code>_parent</code> variable is preloaded before * executing the function. * * @return <code>true</code> if preloaded, else <code>false</code> */ public boolean preloadsParent() { return preloadParent; } /** * Checks whether the <code>_root</code> variable is preloaded before * executing the function. * * @return <code>true</code> if preloaded, else <code>false</code> */ public boolean preloadsRoot() { return preloadRoot; } /** * Checks whether the <code>super</code> variable is preloaded before * executing the function. * * @return <code>true</code> if preloaded, else <code>false</code> */ public boolean preloadsSuper() { return preloadSuper; } /** * Checks whether the <code>this</code> variable is preloaded before * executing the function. * * @return <code>true</code> if preloaded, else <code>false</code> */ public boolean preloadsThis() { return preloadThis; } /** * Instructs the Flash Player to preload the <code>arguments</code> variable. */ public void suppressArguments() { suppressArguments = true; } /** * Instructs the Flash Player to preload the <code>super</code> variable. */ public void suppressSuper() { suppressSuper = true; } /** * Instructs the Flash Player to preload the <code>this</code> variable. */ public void suppressThis() { suppressThis = true; } /** * Checks whether the <code>arguments</code> variable is suppressed. * * @return <code>true</code> if suppressed, else <code>false</code> */ public boolean suppressesArguments() { return suppressArguments; } /** * Checks whether the <code>super</code> variable is suppressed. * * @return <code>true</code> if suppressed, else <code>false</code> */ public boolean suppressesSuper() { return suppressSuper; } /** * Checks whether the <code>this</code> variable is suppressed. * * @return <code>true</code> if suppressed, else <code>false</code> */ public boolean suppressesThis() { return suppressThis; } /** * Returns a short description of this action. * * @return <code>"DefineFunction2"</code>, function name */ public String toString() { return "DefineFunction2 " + name; } protected void writeData( OutputBitStream dataStream, OutputBitStream mainStream) throws IOException { dataStream.writeString(name); dataStream.writeUI16((parameters == null) ? 0 : parameters.length); dataStream.writeUI8(registerCount); dataStream.writeBooleanBit(preloadParent); dataStream.writeBooleanBit(preloadRoot); dataStream.writeBooleanBit(suppressSuper); dataStream.writeBooleanBit(preloadSuper); dataStream.writeBooleanBit(suppressArguments); dataStream.writeBooleanBit(preloadArguments); dataStream.writeBooleanBit(suppressThis); dataStream.writeBooleanBit(preloadThis); dataStream.writeUI8((short) (preloadGlobal ? 1 : 0)); // 7 reserved bits if (parameters != null) { for (int i = 0; i < parameters.length; i++) { parameters[i].write(dataStream); } } dataStream.writeUI16(body.getSize()); body.write(mainStream, false); } }