Build fix.
[WebKit.git] / Source / JavaScriptCore / runtime / Executable.cpp
1 /*
2  * Copyright (C) 2009, 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "Executable.h"
28
29 #include "BytecodeGenerator.h"
30 #include "CodeBlock.h"
31 #include "JIT.h"
32 #include "Parser.h"
33 #include "UStringBuilder.h"
34 #include "Vector.h"
35
36 #if ENABLE(DFG_JIT)
37 #include "DFGByteCodeParser.h"
38 #include "DFGJITCompiler.h"
39 #endif
40
41 namespace JSC {
42
43 const ClassInfo ExecutableBase::s_info = { "Executable", 0, 0, 0 };
44
45 #if ENABLE(JIT)
46 #if ENABLE(DFG_JIT)
47 static bool tryDFGCompile(ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode)
48 {
49 #if ENABLE(DFG_JIT_RESTRICTIONS)
50     // FIXME: No flow control yet supported, don't bother scanning the bytecode if there are any jump targets.
51     if (codeBlock->numberOfJumpTargets())
52         return false;
53 #endif
54
55     JSGlobalData* globalData = &exec->globalData();
56     DFG::Graph dfg(codeBlock->m_numParameters, codeBlock->m_numVars);
57     if (!parse(dfg, globalData, codeBlock))
58         return false;
59
60     DFG::JITCompiler dataFlowJIT(globalData, dfg, codeBlock);
61     dataFlowJIT.compile(jitCode);
62     return true;
63 }
64
65 static bool tryDFGCompileFunction(ExecState* exec, ExecState* calleeArgsExec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr& jitCodeWithArityCheck)
66 {
67 #if ENABLE(DFG_JIT_RESTRICTIONS)
68     // FIXME: No flow control yet supported, don't bother scanning the bytecode if there are any jump targets.
69     if (codeBlock->numberOfJumpTargets())
70         return false;
71 #endif
72
73     JSGlobalData* globalData = &exec->globalData();
74     DFG::Graph dfg(codeBlock->m_numParameters, codeBlock->m_numVars);
75     if (!parse(dfg, globalData, codeBlock))
76         return false;
77
78     if (calleeArgsExec)
79         dfg.predictArgumentTypes(calleeArgsExec);
80
81     DFG::JITCompiler dataFlowJIT(globalData, dfg, codeBlock);
82     dataFlowJIT.compileFunction(jitCode, jitCodeWithArityCheck);
83     return true;
84 }
85 #else
86 static bool tryDFGCompile(ExecState*, CodeBlock*, JITCode&) { return false; }
87 static bool tryDFGCompileFunction(ExecState*, ExecState*, CodeBlock*, JITCode&, MacroAssemblerCodePtr&) { return false; }
88 #endif
89
90 class ExecutableFinalizer : public WeakHandleOwner {
91     virtual void finalize(Handle<Unknown> handle, void*)
92     {
93         Weak<ExecutableBase> executable(Weak<ExecutableBase>::Adopt, handle);
94         executable->clearExecutableCode();
95     }
96 };
97
98 WeakHandleOwner* ExecutableBase::executableFinalizer()
99 {
100     DEFINE_STATIC_LOCAL(ExecutableFinalizer, finalizer, ());
101     return &finalizer;
102 }
103 #endif
104     
105 const ClassInfo NativeExecutable::s_info = { "NativeExecutable", &ExecutableBase::s_info, 0, 0 };
106
107 NativeExecutable::~NativeExecutable()
108 {
109 }
110
111 const ClassInfo ScriptExecutable::s_info = { "ScriptExecutable", &ExecutableBase::s_info, 0, 0 };
112
113 const ClassInfo EvalExecutable::s_info = { "EvalExecutable", &ScriptExecutable::s_info, 0, 0 };
114
115 EvalExecutable::EvalExecutable(ExecState* exec, const SourceCode& source, bool inStrictContext)
116     : ScriptExecutable(exec->globalData().evalExecutableStructure.get(), exec, source, inStrictContext)
117 {
118 }
119
120 EvalExecutable::~EvalExecutable()
121 {
122 }
123
124 const ClassInfo ProgramExecutable::s_info = { "ProgramExecutable", &ScriptExecutable::s_info, 0, 0 };
125
126 ProgramExecutable::ProgramExecutable(ExecState* exec, const SourceCode& source)
127     : ScriptExecutable(exec->globalData().programExecutableStructure.get(), exec, source, false)
128 {
129 }
130
131 ProgramExecutable::~ProgramExecutable()
132 {
133 }
134
135 const ClassInfo FunctionExecutable::s_info = { "FunctionExecutable", &ScriptExecutable::s_info, 0, 0 };
136
137 FunctionExecutable::FunctionExecutable(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext, int firstLine, int lastLine)
138     : ScriptExecutable(globalData.functionExecutableStructure.get(), globalData, source, inStrictContext)
139     , m_numCapturedVariables(0)
140     , m_forceUsesArguments(forceUsesArguments)
141     , m_parameters(parameters)
142     , m_name(name)
143     , m_symbolTable(0)
144 {
145     m_firstLine = firstLine;
146     m_lastLine = lastLine;
147 }
148
149 FunctionExecutable::FunctionExecutable(ExecState* exec, const Identifier& name, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext, int firstLine, int lastLine)
150     : ScriptExecutable(exec->globalData().functionExecutableStructure.get(), exec, source, inStrictContext)
151     , m_numCapturedVariables(0)
152     , m_forceUsesArguments(forceUsesArguments)
153     , m_parameters(parameters)
154     , m_name(name)
155     , m_symbolTable(0)
156 {
157     m_firstLine = firstLine;
158     m_lastLine = lastLine;
159 }
160
161
162 JSObject* EvalExecutable::compileInternal(ExecState* exec, ScopeChainNode* scopeChainNode)
163 {
164     JSObject* exception = 0;
165     JSGlobalData* globalData = &exec->globalData();
166     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
167     if (!lexicalGlobalObject->evalEnabled())
168         return throwError(exec, createEvalError(exec, "Eval is disabled"));
169     RefPtr<EvalNode> evalNode = globalData->parser->parse<EvalNode>(lexicalGlobalObject, lexicalGlobalObject->debugger(), exec, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, &exception);
170     if (!evalNode) {
171         ASSERT(exception);
172         return exception;
173     }
174     recordParse(evalNode->features(), evalNode->hasCapturedVariables(), evalNode->lineNo(), evalNode->lastLine());
175
176     JSGlobalObject* globalObject = scopeChainNode->globalObject.get();
177
178     ASSERT(!m_evalCodeBlock);
179     m_evalCodeBlock = adoptPtr(new EvalCodeBlock(this, globalObject, source().provider(), scopeChainNode->localDepth()));
180     OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(evalNode.get(), scopeChainNode, m_evalCodeBlock->symbolTable(), m_evalCodeBlock.get())));
181     if ((exception = generator->generate())) {
182         m_evalCodeBlock.clear();
183         evalNode->destroyData();
184         return exception;
185     }
186
187     evalNode->destroyData();
188
189 #if ENABLE(JIT)
190     if (exec->globalData().canUseJIT()) {
191         bool dfgCompiled = tryDFGCompile(exec, m_evalCodeBlock.get(), m_jitCodeForCall);
192         if (!dfgCompiled)
193             m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_evalCodeBlock.get());
194 #if !ENABLE(OPCODE_SAMPLING)
195         if (!BytecodeGenerator::dumpsGeneratedCode())
196             m_evalCodeBlock->discardBytecode();
197 #endif
198     }
199 #endif
200
201 #if ENABLE(JIT)
202 #if ENABLE(INTERPRETER)
203     if (!m_jitCodeForCall)
204         Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_evalCodeBlock));
205     else
206 #endif
207     Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_evalCodeBlock) + m_jitCodeForCall.size());
208 #else
209     Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_evalCodeBlock));
210 #endif
211
212     return 0;
213 }
214
215 void EvalExecutable::visitChildren(SlotVisitor& visitor)
216 {
217     ASSERT_GC_OBJECT_INHERITS(this, &s_info);
218     COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
219     ASSERT(structure()->typeInfo().overridesVisitChildren());
220     ScriptExecutable::visitChildren(visitor);
221     if (m_evalCodeBlock)
222         m_evalCodeBlock->visitAggregate(visitor);
223 }
224
225 void EvalExecutable::unlinkCalls()
226 {
227 #if ENABLE(JIT)
228     if (!m_jitCodeForCall)
229         return;
230     ASSERT(m_evalCodeBlock);
231     m_evalCodeBlock->unlinkCalls();
232 #endif
233 }
234
235 JSObject* ProgramExecutable::checkSyntax(ExecState* exec)
236 {
237     JSObject* exception = 0;
238     JSGlobalData* globalData = &exec->globalData();
239     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
240     RefPtr<ProgramNode> programNode = globalData->parser->parse<ProgramNode>(lexicalGlobalObject, lexicalGlobalObject->debugger(), exec, m_source, 0, JSParseNormal, &exception);
241     if (programNode)
242         return 0;
243     ASSERT(exception);
244     return exception;
245 }
246
247 JSObject* ProgramExecutable::compileInternal(ExecState* exec, ScopeChainNode* scopeChainNode)
248 {
249     ASSERT(!m_programCodeBlock);
250
251     JSObject* exception = 0;
252     JSGlobalData* globalData = &exec->globalData();
253     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
254     RefPtr<ProgramNode> programNode = globalData->parser->parse<ProgramNode>(lexicalGlobalObject, lexicalGlobalObject->debugger(), exec, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, &exception);
255     if (!programNode) {
256         ASSERT(exception);
257         return exception;
258     }
259     recordParse(programNode->features(), programNode->hasCapturedVariables(), programNode->lineNo(), programNode->lastLine());
260
261     JSGlobalObject* globalObject = scopeChainNode->globalObject.get();
262     
263     m_programCodeBlock = adoptPtr(new ProgramCodeBlock(this, GlobalCode, globalObject, source().provider()));
264     OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(programNode.get(), scopeChainNode, &globalObject->symbolTable(), m_programCodeBlock.get())));
265     if ((exception = generator->generate())) {
266         m_programCodeBlock.clear();
267         programNode->destroyData();
268         return exception;
269     }
270
271     programNode->destroyData();
272
273 #if ENABLE(JIT)
274     if (exec->globalData().canUseJIT()) {
275         bool dfgCompiled = tryDFGCompile(exec, m_programCodeBlock.get(), m_jitCodeForCall);
276         if (!dfgCompiled)
277             m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_programCodeBlock.get());
278 #if !ENABLE(OPCODE_SAMPLING)
279         if (!BytecodeGenerator::dumpsGeneratedCode())
280             m_programCodeBlock->discardBytecode();
281 #endif
282     }
283 #endif
284
285 #if ENABLE(JIT)
286 #if ENABLE(INTERPRETER)
287     if (!m_jitCodeForCall)
288         Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_programCodeBlock));
289     else
290 #endif
291         Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_programCodeBlock) + m_jitCodeForCall.size());
292 #else
293     Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_programCodeBlock));
294 #endif
295
296     return 0;
297 }
298
299 void ProgramExecutable::unlinkCalls()
300 {
301 #if ENABLE(JIT)
302     if (!m_jitCodeForCall)
303         return;
304     ASSERT(m_programCodeBlock);
305     m_programCodeBlock->unlinkCalls();
306 #endif
307 }
308
309 void ProgramExecutable::visitChildren(SlotVisitor& visitor)
310 {
311     ASSERT_GC_OBJECT_INHERITS(this, &s_info);
312     COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
313     ASSERT(structure()->typeInfo().overridesVisitChildren());
314     ScriptExecutable::visitChildren(visitor);
315     if (m_programCodeBlock)
316         m_programCodeBlock->visitAggregate(visitor);
317 }
318
319 JSObject* FunctionExecutable::compileForCallInternal(ExecState* exec, ScopeChainNode* scopeChainNode, ExecState* calleeArgsExec)
320 {
321     JSObject* exception = 0;
322     JSGlobalData* globalData = scopeChainNode->globalData;
323     RefPtr<FunctionBodyNode> body = globalData->parser->parse<FunctionBodyNode>(exec->lexicalGlobalObject(), 0, 0, m_source, m_parameters.get(), isStrictMode() ? JSParseStrict : JSParseNormal, &exception);
324     if (!body) {
325         ASSERT(exception);
326         return exception;
327     }
328     if (m_forceUsesArguments)
329         body->setUsesArguments();
330     body->finishParsing(m_parameters, m_name);
331     recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine());
332
333     JSGlobalObject* globalObject = scopeChainNode->globalObject.get();
334
335     ASSERT(!m_codeBlockForCall);
336     m_codeBlockForCall = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), false));
337     OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChainNode, m_codeBlockForCall->symbolTable(), m_codeBlockForCall.get())));
338     if ((exception = generator->generate())) {
339         m_codeBlockForCall.clear();
340         body->destroyData();
341         return exception;
342     }
343
344     m_numParametersForCall = m_codeBlockForCall->m_numParameters;
345     ASSERT(m_numParametersForCall);
346     m_numCapturedVariables = m_codeBlockForCall->m_numCapturedVars;
347     m_symbolTable = m_codeBlockForCall->sharedSymbolTable();
348
349     body->destroyData();
350
351 #if ENABLE(JIT)
352     if (exec->globalData().canUseJIT()) {
353         bool dfgCompiled = tryDFGCompileFunction(exec, calleeArgsExec, m_codeBlockForCall.get(), m_jitCodeForCall, m_jitCodeForCallWithArityCheck);
354         if (!dfgCompiled)
355             m_jitCodeForCall = JIT::compile(scopeChainNode->globalData, m_codeBlockForCall.get(), &m_jitCodeForCallWithArityCheck);
356
357 #if !ENABLE(OPCODE_SAMPLING)
358         if (!BytecodeGenerator::dumpsGeneratedCode())
359             m_codeBlockForCall->discardBytecode();
360 #endif
361     }
362 #else
363     UNUSED_PARAM(calleeArgsExec);
364 #endif
365
366 #if ENABLE(JIT)
367 #if ENABLE(INTERPRETER)
368     if (!m_jitCodeForCall)
369         Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForCall));
370     else
371 #endif
372         Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForCall) + m_jitCodeForCall.size());
373 #else
374     Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForCall));
375 #endif
376
377     return 0;
378 }
379
380 JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, ScopeChainNode* scopeChainNode)
381 {
382     JSObject* exception = 0;
383     JSGlobalData* globalData = scopeChainNode->globalData;
384     RefPtr<FunctionBodyNode> body = globalData->parser->parse<FunctionBodyNode>(exec->lexicalGlobalObject(), 0, 0, m_source, m_parameters.get(), isStrictMode() ? JSParseStrict : JSParseNormal, &exception);
385     if (!body) {
386         ASSERT(exception);
387         return exception;
388     }
389     if (m_forceUsesArguments)
390         body->setUsesArguments();
391     body->finishParsing(m_parameters, m_name);
392     recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine());
393
394     JSGlobalObject* globalObject = scopeChainNode->globalObject.get();
395
396     ASSERT(!m_codeBlockForConstruct);
397     m_codeBlockForConstruct = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), true));
398     OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChainNode, m_codeBlockForConstruct->symbolTable(), m_codeBlockForConstruct.get())));
399     if ((exception = generator->generate())) {
400         m_codeBlockForConstruct.clear();
401         body->destroyData();
402         return exception;
403     }
404
405     m_numParametersForConstruct = m_codeBlockForConstruct->m_numParameters;
406     ASSERT(m_numParametersForConstruct);
407     m_numCapturedVariables = m_codeBlockForConstruct->m_numCapturedVars;
408     m_symbolTable = m_codeBlockForConstruct->sharedSymbolTable();
409
410     body->destroyData();
411
412 #if ENABLE(JIT)
413     if (exec->globalData().canUseJIT()) {
414         m_jitCodeForConstruct = JIT::compile(scopeChainNode->globalData, m_codeBlockForConstruct.get(), &m_jitCodeForConstructWithArityCheck);
415 #if !ENABLE(OPCODE_SAMPLING)
416         if (!BytecodeGenerator::dumpsGeneratedCode())
417             m_codeBlockForConstruct->discardBytecode();
418 #endif
419     }
420 #endif
421
422 #if ENABLE(JIT)
423 #if ENABLE(INTERPRETER)
424     if (!m_jitCodeForConstruct)
425         Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForConstruct));
426     else
427 #endif
428     Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForConstruct) + m_jitCodeForConstruct.size());
429 #else
430     Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForConstruct));
431 #endif
432
433     return 0;
434 }
435
436 void FunctionExecutable::visitChildren(SlotVisitor& visitor)
437 {
438     ASSERT_GC_OBJECT_INHERITS(this, &s_info);
439     COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
440     ASSERT(structure()->typeInfo().overridesVisitChildren());
441     ScriptExecutable::visitChildren(visitor);
442     if (m_codeBlockForCall)
443         m_codeBlockForCall->visitAggregate(visitor);
444     if (m_codeBlockForConstruct)
445         m_codeBlockForConstruct->visitAggregate(visitor);
446 }
447
448 void FunctionExecutable::discardCode()
449 {
450 #if ENABLE(JIT)
451     // These first two checks are to handle the rare case where
452     // we are trying to evict code for a function during its
453     // codegen.
454     if (!m_jitCodeForCall && m_codeBlockForCall)
455         return;
456     if (!m_jitCodeForConstruct && m_codeBlockForConstruct)
457         return;
458     m_jitCodeForCall = JITCode();
459     m_jitCodeForConstruct = JITCode();
460     m_jitCodeForCallWithArityCheck = MacroAssemblerCodePtr();
461     m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr();
462 #endif
463     if (m_codeBlockForCall)
464         m_codeBlockForCall->clearEvalCache();
465     m_codeBlockForCall.clear();
466     if (m_codeBlockForConstruct)
467         m_codeBlockForConstruct->clearEvalCache();
468     m_codeBlockForConstruct.clear();
469     m_numParametersForCall = NUM_PARAMETERS_NOT_COMPILED;
470     m_numParametersForConstruct = NUM_PARAMETERS_NOT_COMPILED;
471
472 }
473
474 void FunctionExecutable::unlinkCalls()
475 {
476 #if ENABLE(JIT)
477     if (!!m_jitCodeForCall) {
478         ASSERT(m_codeBlockForCall);
479         m_codeBlockForCall->unlinkCalls();
480     }
481     if (!!m_jitCodeForConstruct) {
482         ASSERT(m_codeBlockForConstruct);
483         m_codeBlockForConstruct->unlinkCalls();
484     }
485 #endif
486 }
487
488 FunctionExecutable* FunctionExecutable::fromGlobalCode(const Identifier& functionName, ExecState* exec, Debugger* debugger, const SourceCode& source, JSObject** exception)
489 {
490     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
491     RefPtr<ProgramNode> program = exec->globalData().parser->parse<ProgramNode>(lexicalGlobalObject, debugger, exec, source, 0, JSParseNormal, exception);
492     if (!program) {
493         ASSERT(*exception);
494         return 0;
495     }
496
497     // Uses of this function that would not result in a single function expression are invalid.
498     StatementNode* exprStatement = program->singleStatement();
499     ASSERT(exprStatement);
500     ASSERT(exprStatement->isExprStatement());
501     ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr();
502     ASSERT(funcExpr);
503     ASSERT(funcExpr->isFuncExprNode());
504     FunctionBodyNode* body = static_cast<FuncExprNode*>(funcExpr)->body();
505     ASSERT(body);
506
507     return FunctionExecutable::create(exec->globalData(), functionName, body->source(), body->usesArguments(), body->parameters(), body->isStrictMode(), body->lineNo(), body->lastLine());
508 }
509
510 UString FunctionExecutable::paramString() const
511 {
512     FunctionParameters& parameters = *m_parameters;
513     UStringBuilder builder;
514     for (size_t pos = 0; pos < parameters.size(); ++pos) {
515         if (!builder.isEmpty())
516             builder.append(", ");
517         builder.append(parameters[pos].ustring());
518     }
519     return builder.toUString();
520 }
521
522 }