Commits

Stephen McKamey committed ecd453e

- cleaning up external refs story
- should now better track changes to external refs and emit to client when assigned

Comments (0)

Files changed (10)

src/org/duelengine/duel/DataEncoder.java

 
 		// emit as a code block of var declarations
 		List<String> namespaces = new ArrayList<String>();
-		for (Map.Entry<String, Object> globalVar : vars.entrySet()) {
-			String key = globalVar.getKey();
+		for (Map.Entry<String, Object> externalVar : vars.entrySet()) {
+			String key = externalVar.getKey();
 			this.writeNamespace(output, namespaces, key);
 
 			if (key.indexOf('.') < 0) {
 			if (this.prettyPrint) {
 				output.append(' ');
 			}
-			this.write(output, globalVar.getValue());
+			this.write(output, externalVar.getValue());
 			output.append(';');
 			this.writeln(output, 0);
 		}

src/org/duelengine/duel/DuelContext.java

 package org.duelengine.duel;
 
+import java.util.*;
+
 /**
  * Maintains context state for a single request/response cycle.
  * DuelContext is not thread-safe and not intended to be reusable.
 	private DataEncoder encoder;
 	private String newline;
 	private String indent;
-	private boolean encodeNonASCII = true;
+	private boolean encodeNonASCII;
 
-	private boolean globalsPending;
-	private SparseMap globals;
+	private enum ExternalsState {
+
+		/**
+		 * No external data
+		 */
+		NONE,
+
+		/**
+		 * Never emitted
+		 */
+		PENDING,
+
+		/**
+		 * Emitted
+		 */
+		EMITTED,
+
+		/**
+		 * Changed after emitted 
+		 */
+		DIRTY
+	}
+
+	private ExternalsState externalsState = ExternalsState.NONE;
+	private SparseMap externals;
+	private SparseMap dirty;
 
 	public DuelContext(Appendable output) {
 		if (output == null) {
 		this.encodeNonASCII = value;
 	}
 
-	public void putGlobal(String ident, Object value) {
+	public void putExternal(String ident, Object value) {
 		if (ident == null) {
 			throw new NullPointerException("ident");
 		}
 
-		if (this.globals == null) {
-			this.globals = new SparseMap();
+		if (this.externals == null) {
+			this.externals = new SparseMap();
 		}
+		this.externals.putSparse(ident, value);
 
-		this.globalsPending = true;
-		this.globals.putSparse(ident, value);
+		switch (this.externalsState) {
+			case NONE:
+			case PENDING:
+				this.externalsState = ExternalsState.PENDING;
+				break;
+			case EMITTED:
+			case DIRTY:
+				this.externalsState = ExternalsState.DIRTY;
+				if (this.dirty == null) {
+					this.dirty = new SparseMap();
+				}
+				// also add to dirty set
+				this.dirty.putSparse(ident, value);
+				break;
+		}
 	}
 
-	boolean hasGlobals(String... idents) {
-		if (this.globals == null) {
+	boolean hasExternals(String... idents) {
+		if (this.externals == null) {
 			return false;
 		}
 
 		if (idents != null) {
 			for (String ident : idents) {
-				if (!this.globals.containsKey(ident)) {
+				if (!this.externals.containsKey(ident)) {
 					return false;
 				}
 			}
 		return true;
 	}
 
-	Object getGlobal(String ident) {
+	Object getExternal(String ident) {
 		if (ident == null) {
 			throw new NullPointerException("ident");
 		}
 
-		if (this.globals == null || !this.globals.containsKey(ident)) {
+		if (this.externals == null || !this.externals.containsKey(ident)) {
 			return null;
 		}
 
-		return this.globals.get(ident);
+		return this.externals.get(ident);
 	}
 
-	SparseMap getGlobals() {
-		return this.globals;
+	SparseMap getPendingExternals() {
+
+		SparseMap sparseMap = (this.externalsState == ExternalsState.DIRTY) ? this.dirty : this.externals;
+		this.externalsState = ExternalsState.EMITTED;
+		this.dirty = null;
+		return sparseMap;
 	}
 
-	boolean isGlobalsPending() {
-		return this.globalsPending;
-	}
-
-	void setGlobalsPending(boolean value) {
-		this.globalsPending = value;
+	boolean hasExternalsPending() {
+		switch (this.externalsState) {
+			case PENDING:
+			case DIRTY:
+				return true;
+			default:
+				return false;
+		}
 	}
 
 	Appendable getOutput() {

src/org/duelengine/duel/DuelView.java

 		context.getEncoder().write(context.getOutput(), data, depth);
 	}
 
-	protected Object getGlobal(DuelContext context, String ident) {
-		return context.getGlobal(ident);
+	protected Object getExternal(DuelContext context, String ident) {
+		return context.getExternal(ident);
 	}
 
-	protected void putGlobal(DuelContext context, String ident, Object value) {
-		context.putGlobal(ident, value);
+	protected void putExternal(DuelContext context, String ident, Object value) {
+		context.putExternal(ident, value);
 	}
 
-	protected boolean hasGlobals(DuelContext context, String... idents) {
-		return context.hasGlobals(idents);
+	protected boolean hasExternals(DuelContext context, String... idents) {
+		return context.hasExternals(idents);
 	}
 
-	protected void writeGlobals(DuelContext context, boolean needsTags)
+	protected void writeExternals(DuelContext context, boolean needsTags)
 		throws IOException {
 
-		if (!context.isGlobalsPending()) {
+		if (!context.hasExternalsPending()) {
 			return;
 		}
 
 				.writeAttribute(output, "type", "text/javascript")
 				.writeCloseElementBeginTag(output);
 		}
-		context.getEncoder().writeVars(output, context.getGlobals());
+		context.getEncoder().writeVars(output, context.getPendingExternals());
 		if (needsTags) {
 			formatter.writeElementEndTag(output, "script");
 		}
-		context.setGlobalsPending(false);
 	}
 
 	protected String nextID(DuelContext context) {

src/org/duelengine/duel/codedom/ScriptVariableReferenceExpression.java

 package org.duelengine.duel.codedom;
 
 /**
- * Represents a Global variable which is only present in the client 
+ * Represents an external variable which is defined outside the model data 
  */
 public class ScriptVariableReferenceExpression extends ScriptExpression {
 

src/org/duelengine/duel/codegen/CodeDOMBuilder.java

 	private final Stack<CodeStatementCollection> scopeStack = new Stack<CodeStatementCollection>();
 	private CodeTypeDeclaration viewType;
 	private TagMode tagMode;
-	private boolean globalsEmitted;
+	private boolean needsExternalsEmitted;
+	private boolean hasScripts;
 
 	public CodeDOMBuilder() {
 		this(null);
 
 			this.scopeStack.clear();
 			this.tagMode = TagMode.None;
-			this.globalsEmitted = false;
+			this.hasScripts = false;
+			this.needsExternalsEmitted = true;
 			this.viewType = CodeDOMUtility.createViewType(ns, name);
 
 			CodeMethod method = this.buildRenderMethod(viewNode.getChildren()).withOverride();
 			// convert from JavaScript source to CodeDOM
 			List<CodeMember> members = new ScriptTranslator(this.viewType).translate(node.getClientCode());
 			boolean firstIsMethod = (members.size() > 0) && members.get(0) instanceof CodeMethod;
+			CodeMethod method = firstIsMethod ? (CodeMethod)members.get(0) : null;
 
 			CodeExpression expression = null;
 			if (firstIsMethod) {
 				// attempt to extract single expression (inline the return expression)
-				expression = CodeDOMUtility.inlineMethod((CodeMethod)members.get(0));
+				expression = CodeDOMUtility.inlineMethod(method);
 			}
 
 			if (expression != null) {
 				// add all CodeDOM members to viewType
 				this.viewType.addAll(members);
 
-				CodeMethod method = (CodeMethod)members.get(0);
-
 				// have the expression be a method invocation
 				expression = new CodeMethodInvokeExpression(
 					method.getReturnType(),
 					new CodeVariableReferenceExpression(String.class, "key"));
 			}
 
-			if (canWrite && members.size() > 0 &&
-				members.get(0).getUserData(ScriptTranslator.EXTERNAL_REFS) instanceof Object[]) {
+			if (method.getUserData(ScriptTranslator.EXTERNAL_ASSIGN) != null) {
+				// flag as potentially modifying external values
+				this.needsExternalsEmitted = true;
+			}
+
+			if (canWrite && (method != null) &&
+				method.getUserData(ScriptTranslator.EXTERNAL_REFS) instanceof Object[]) {
+
+				// flag as potentially modifying external values
+				this.needsExternalsEmitted = true;
 
 				Object[] refs = (Object[])members.get(0).getUserData(ScriptTranslator.EXTERNAL_REFS);
 				CodeExpression[] args = new CodeExpression[refs.length+1];
 					new CodeMethodInvokeExpression(
 						boolean.class,
 						new CodeThisReferenceExpression(),
-						"hasGlobals",
+						"hasExternals",
 						args));
 
 				if (firstIsMethod && expression instanceof CodeMethodInvokeExpression) {
-					CodeMethod method = (CodeMethod)members.get(0);
 					runtimeCheck.getTrueStatements().addAll(method.getStatements());
 					this.viewType.getMembers().remove(method);
 				} else {
 		CodeStatementCollection scope = this.scopeStack.peek();
 
 		// use the script tag as its own replacement element
-		this.formatter.writeOpenElementBeginTag(this.buffer, "script");
-		this.formatter.writeAttribute(this.buffer, "type", "text/javascript");
-		this.formatter.writeCloseElementBeginTag(this.buffer);
-		this.ensureGlobalsEmitted(false);
+		this.hasScripts = true;
+		this.formatter
+			.writeOpenElementBeginTag(this.buffer, "script")
+			.writeAttribute(this.buffer, "type", "text/javascript")
+			.writeCloseElementBeginTag(this.buffer);
+		this.ensureExternalsEmitted(false);
 
 		// emit patch function call which serializes attributes into object
 		this.buffer.append("duel.write(");
 		String tagName = element.getTagName();
 
 		if ("script".equalsIgnoreCase(tagName)) {
-			this.ensureGlobalsEmitted(true);
+			this.hasScripts = true;
+			this.ensureExternalsEmitted(true);
 		}
 		
 		this.formatter.writeOpenElementBeginTag(this.buffer, tagName);
 				this.tagMode = prevMode;
 			}
 
+			// ensure changes emitted, if no scripts then no need
+			if (this.hasScripts && "body".equalsIgnoreCase(tagName)) {
+				this.ensureExternalsEmitted(true);
+				this.buffer.append(this.settings.getNewline());
+			}
 			this.formatter.writeElementEndTag(this.buffer, tagName);
 
 		} else {
 		CodeStatementCollection scope = this.scopeStack.peek();
 
 		// execute any deferred attributes using idVar
+		this.hasScripts = true;
 		this.formatter
 			.writeOpenElementBeginTag(this.buffer, "script")
 			.writeAttribute(this.buffer, "type", "text/javascript")
 			.writeCloseElementBeginTag(this.buffer);
-		this.ensureGlobalsEmitted(false);
+		this.ensureExternalsEmitted(false);
 
 		// emit patch function call which serializes attributes into object
 		this.buffer.append("duel.attr(");
 		CodeStatementCollection scope = this.scopeStack.peek();
 
 		// use the script tag as its own replacement element
-		this.formatter.writeOpenElementBeginTag(this.buffer, "script");
-		this.formatter.writeAttribute(this.buffer, "type", "text/javascript");
-		this.formatter.writeOpenAttribute(this.buffer, "id");
+		this.hasScripts = true;
+		this.formatter
+			.writeOpenElementBeginTag(this.buffer, "script")
+			.writeAttribute(this.buffer, "type", "text/javascript")
+			.writeOpenAttribute(this.buffer, "id");
 		CodeVariableDeclarationStatement idVar = this.emitClientID();
-		this.formatter.writeCloseAttribute(this.buffer);
-		this.formatter.writeCloseElementBeginTag(this.buffer);
-		this.ensureGlobalsEmitted(false);
+		this.formatter
+			.writeCloseAttribute(this.buffer)
+			.writeCloseElementBeginTag(this.buffer);
+		this.ensureExternalsEmitted(false);
 
 		// emit patch function call which serializes attributes into object
 		this.buffer.append("duel.replace(");
 			CodeDOMUtility.emitExpression(codeExpr);
 	}
 
-	private void ensureGlobalsEmitted(boolean needsTags) {
-		if (this.globalsEmitted) {
+	private void ensureExternalsEmitted(boolean needsTags) {
+		if (!this.needsExternalsEmitted) {
 			return;
 		}
 
 			new CodeMethodInvokeExpression(
 				Void.class,
 				new CodeThisReferenceExpression(),
-				"writeGlobals",
+				"writeExternals",
 				new CodeVariableReferenceExpression(DuelContext.class, "context"),
 				new CodePrimitiveExpression(needsTags)));
 
 		// might prevent this from being emitted. if found do not mark.
 		// there is a runtime check as well which will prevent multiple.
 		for (CodeStatementCollection scope : this.scopeStack) {
-			// TODO: this is too naive. doesn't work with hybrid methods
+			// this is too naive. may not work with inlined methods
 			if (!(scope.getOwner() instanceof CodeMethod)) {
 				return;
 			}
 		}
 
-		this.globalsEmitted = true;
+		this.needsExternalsEmitted = false;
 	}
 
 	private CodeMethod ensureInitMethod() {

src/org/duelengine/duel/codegen/CodeDOMUtility.java

 		return new CodeMethodInvokeExpression(
 			Object.class,
 			new CodeThisReferenceExpression(),
-			"getGlobal",
+			"getExternal",
 			new CodeVariableReferenceExpression(DuelContext.class, "context"),
 			new CodePrimitiveExpression(ident));
 	}

src/org/duelengine/duel/codegen/ScriptTranslator.java

 public class ScriptTranslator implements ErrorReporter {
 
 	public static final String EXTERNAL_REFS = "EXTERNAL_REFS";
+	public static final String EXTERNAL_ASSIGN = "EXTERNAL_ASSIGN";
 
 	private final IdentifierScope scope;
 	private List<String> externalRefs;
+	private boolean externalAssign;
 
 	public ScriptTranslator() {
 		this(new CodeTypeDeclaration());
 	public List<CodeMember> translate(String jsSource) {
 
 		this.externalRefs = null;
+		this.externalAssign = false;
 		String jsFilename = "anonymous.js";
 		ErrorReporter errorReporter = this;
 
 		}
 
 		List<CodeMember> members = this.visitRoot(root);
-		if (this.externalRefs != null && members.size() > 0) {
-			// store external identifiers on the member to allow generation of a fallback block
-			members.get(0).withUserData(EXTERNAL_REFS, this.externalRefs.toArray());
+		if (members.size() > 0) {
+			CodeMember method = members.get(0);
+			if (this.externalRefs != null) {
+				// store external identifiers on the member to allow generation of a fallback block
+				method.withUserData(EXTERNAL_REFS, this.externalRefs.toArray());
+			}
+			if (this.externalAssign) {
+				// flag as potentially modifying values
+				method.withUserData(EXTERNAL_ASSIGN, true);
+			}
 		}
 		return members;
 	}
 				this.externalRefs = new ArrayList<String>();
 			}
 			this.externalRefs.add(ident);
+		} else {
+			this.externalAssign = true;
 		}
 
 		return new ScriptVariableReferenceExpression(ident);

src/org/duelengine/duel/codegen/ServerCodeGen.java

 			}
 
 		} else if (expression instanceof ScriptVariableReferenceExpression) {
-			this.writeGlobalDataReference(output, (ScriptVariableReferenceExpression)expression);
+			this.writeExternalDataReference(output, (ScriptVariableReferenceExpression)expression);
 			
 		} else if (expression != null) {
 			throw new UnsupportedOperationException("Unexpected expression: "+expression.getClass());
 		CodeExpression right = expression.getRight();
 
 		boolean leftIsPropertyRef = (left instanceof CodePropertyReferenceExpression);
-		boolean leftIsGlobalRef = (left instanceof ScriptVariableReferenceExpression);
+		boolean leftIsExternalRef = (left instanceof ScriptVariableReferenceExpression);
 
 		String operator;
 		switch (expression.getOperator()) {
 				operator = " - ";
 				break;
 			case SUBTRACT_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.SUBTRACT,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 				operator = " * ";
 				break;
 			case MULTIPLY_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.MULTIPLY,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 				operator = " / ";
 				break;
 			case DIVIDE_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.DIVIDE,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 				operator = " % ";
 				break;
 			case MODULUS_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.MODULUS,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 				operator = " & ";
 				break;
 			case BITWISE_AND_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.BITWISE_AND,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 				operator = " | ";
 				break;
 			case BITWISE_OR_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.BITWISE_OR,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 				operator = " ^ ";
 				break;
 			case BITWISE_XOR_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.BITWISE_XOR,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 				operator = " << ";
 				break;
 			case SHIFT_LEFT_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.SHIFT_LEFT,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 				operator = " >> ";
 				break;
 			case SHIFT_RIGHT_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.SHIFT_RIGHT,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 				operator = " >>> ";
 				break;
 			case USHIFT_RIGHT_ASSIGN:
-				if (leftIsPropertyRef || leftIsGlobalRef || !CodeDOMUtility.isNumber(left)) {
+				if (leftIsPropertyRef || leftIsExternalRef || !CodeDOMUtility.isNumber(left)) {
 					this.writeExpression(output, CodeDOMUtility.asAssignment(CodeBinaryOperatorType.USHIFT_RIGHT,
 						left, CodeDOMUtility.ensureNumber(left), CodeDOMUtility.ensureNumber(right)));
 					return;
 						leftPropRef.getPropertyName(),
 						right));
 				return;
-			} else if (leftIsGlobalRef) {
+			} else if (leftIsExternalRef) {
 				// translate into dynamic helper method call
 				ScriptVariableReferenceExpression leftVarRef = (ScriptVariableReferenceExpression)left; 
 				this.writeExpression(
 					new CodeMethodInvokeExpression(
 						expression.getResultType(),
 						new CodeThisReferenceExpression(),
-						"putGlobal",
+						"putExternal",
 						new CodeVariableReferenceExpression(DuelContext.class, "context"),
 						new CodePrimitiveExpression(leftVarRef.getIdent()),
 						right));
 		return true;
 	}
 
-	private void writeGlobalDataReference(Appendable output, ScriptVariableReferenceExpression expression)
+	private void writeExternalDataReference(Appendable output, ScriptVariableReferenceExpression expression)
 		throws IOException {
 
 		this.writeExpression(output, CodeDOMUtility.lookupExternalVar(expression.getIdent()));

test/org/duelengine/duel/codegen/CodeDOMBuilderTests.java

 				new CodeExpressionStatement(new CodeMethodInvokeExpression(
 					Void.class,
 					new CodeThisReferenceExpression(),
-					"writeGlobals",
+					"writeExternals",
 					new CodeVariableReferenceExpression(DuelContext.class, "context"),
 					new CodePrimitiveExpression(true))),
 				new CodeExpressionStatement(new CodeMethodInvokeExpression(
 				new CodeExpressionStatement(new CodeMethodInvokeExpression(
 					Void.class,
 					new CodeThisReferenceExpression(),
-					"writeGlobals",
+					"writeExternals",
 					new CodeVariableReferenceExpression(DuelContext.class, "context"),
 					new CodePrimitiveExpression(false))),
 				new CodeExpressionStatement(new CodeMethodInvokeExpression(
 				new CodeExpressionStatement(new CodeMethodInvokeExpression(
 					Void.class,
 					new CodeThisReferenceExpression(),
-					"writeGlobals",
+					"writeExternals",
 					new CodeVariableReferenceExpression(DuelContext.class, "context"),
 					new CodePrimitiveExpression(false))),
 				new CodeExpressionStatement(new CodeMethodInvokeExpression(
 					new CodeMethodInvokeExpression(
 						boolean.class,
 						new CodeThisReferenceExpression(),
-						"hasGlobals",
+						"hasExternals",
 						new CodeVariableReferenceExpression(DuelContext.class, "context"),
 						new CodePrimitiveExpression("foo")),
 					new CodeStatement[] {
 						new CodeExpressionStatement(new CodeMethodInvokeExpression(
 							Void.class,
 							new CodeThisReferenceExpression(),
-							"writeGlobals",
+							"writeExternals",
 							new CodeVariableReferenceExpression(DuelContext.class, "context"),
 							new CodePrimitiveExpression(false))),
 						new CodeExpressionStatement(new CodeMethodInvokeExpression(
 					new CodeMethodInvokeExpression(
 						boolean.class,
 						new CodeThisReferenceExpression(),
-						"hasGlobals",
+						"hasExternals",
 						new CodeVariableReferenceExpression(DuelContext.class, "context"),
 						new CodePrimitiveExpression("foo"),
 						new CodePrimitiveExpression("baz")),
 						new CodeExpressionStatement(new CodeMethodInvokeExpression(
 							Void.class,
 							new CodeThisReferenceExpression(),
-							"writeGlobals",
+							"writeExternals",
 							new CodeVariableReferenceExpression(DuelContext.class, "context"),
 							new CodePrimitiveExpression(false))),
 						new CodeExpressionStatement(new CodeMethodInvokeExpression(

test/org/duelengine/duel/codegen/ServerCodeGenTests.java

 			"\t\tthis.write(context, \"</div>\");\n"+
 			"\t}\n\n"+
 			"\tprivate Object code_2(DuelContext context, Object data, int index, int count, String key) {\n"+
-			"\t\tthis.putGlobal(context, \"foo\", data);\n"+
+			"\t\tthis.putExternal(context, \"foo\", data);\n"+
 			"\t\treturn null;\n"+
 			"\t}\n"+
 			"}\n";