11bf6b35041318689fe7bc131c02339d877dcd80
[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 #endif
363
364 #if ENABLE(JIT)
365 #if ENABLE(INTERPRETER)
366     if (!m_jitCodeForCall)
367         Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForCall));
368     else
369 #endif
370         Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForCall) + m_jitCodeForCall.size());
371 #else
372     Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForCall));
373 #endif
374
375     return 0;
376 }
377
378 JSObject* FunctionExecutable::compileForConstructInternal(ExecState* exec, ScopeChainNode* scopeChainNode)
379 {
380     JSObject* exception = 0;
381     JSGlobalData* globalData = scopeChainNode->globalData;
382     RefPtr<FunctionBodyNode> body = globalData->parser->parse<FunctionBodyNode>(exec->lexicalGlobalObject(), 0, 0, m_source, m_parameters.get(), isStrictMode() ? JSParseStrict : JSParseNormal, &exception);
383     if (!body) {
384         ASSERT(exception);
385         return exception;
386     }
387     if (m_forceUsesArguments)
388         body->setUsesArguments();
389     body->finishParsing(m_parameters, m_name);
390     recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine());
391
392     JSGlobalObject* globalObject = scopeChainNode->globalObject.get();
393
394     ASSERT(!m_codeBlockForConstruct);
395     m_codeBlockForConstruct = adoptPtr(new FunctionCodeBlock(this, FunctionCode, globalObject, source().provider(), source().startOffset(), true));
396     OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(body.get(), scopeChainNode, m_codeBlockForConstruct->symbolTable(), m_codeBlockForConstruct.get())));
397     if ((exception = generator->generate())) {
398         m_codeBlockForConstruct.clear();
399         body->destroyData();
400         return exception;
401     }
402
403     m_numParametersForConstruct = m_codeBlockForConstruct->m_numParameters;
404     ASSERT(m_numParametersForConstruct);
405     m_numCapturedVariables = m_codeBlockForConstruct->m_numCapturedVars;
406     m_symbolTable = m_codeBlockForConstruct->sharedSymbolTable();
407
408     body->destroyData();
409
410 #if ENABLE(JIT)
411     if (exec->globalData().canUseJIT()) {
412         m_jitCodeForConstruct = JIT::compile(scopeChainNode->globalData, m_codeBlockForConstruct.get(), &m_jitCodeForConstructWithArityCheck);
413 #if !ENABLE(OPCODE_SAMPLING)
414         if (!BytecodeGenerator::dumpsGeneratedCode())
415             m_codeBlockForConstruct->discardBytecode();
416 #endif
417     }
418 #endif
419
420 #if ENABLE(JIT)
421 #if ENABLE(INTERPRETER)
422     if (!m_jitCodeForConstruct)
423         Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForConstruct));
424     else
425 #endif
426     Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForConstruct) + m_jitCodeForConstruct.size());
427 #else
428     Heap::heap(this)->reportExtraMemoryCost(sizeof(*m_codeBlockForConstruct));
429 #endif
430
431     return 0;
432 }
433
434 void FunctionExecutable::visitChildren(SlotVisitor& visitor)
435 {
436     ASSERT_GC_OBJECT_INHERITS(this, &s_info);
437     COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
438     ASSERT(structure()->typeInfo().overridesVisitChildren());
439     ScriptExecutable::visitChildren(visitor);
440     if (m_codeBlockForCall)
441         m_codeBlockForCall->visitAggregate(visitor);
442     if (m_codeBlockForConstruct)
443         m_codeBlockForConstruct->visitAggregate(visitor);
444 }
445
446 void FunctionExecutable::discardCode()
447 {
448 #if ENABLE(JIT)
449     // These first two checks are to handle the rare case where
450     // we are trying to evict code for a function during its
451     // codegen.
452     if (!m_jitCodeForCall && m_codeBlockForCall)
453         return;
454     if (!m_jitCodeForConstruct && m_codeBlockForConstruct)
455         return;
456     m_jitCodeForCall = JITCode();
457     m_jitCodeForConstruct = JITCode();
458     m_jitCodeForCallWithArityCheck = MacroAssemblerCodePtr();
459     m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr();
460 #endif
461     if (m_codeBlockForCall)
462         m_codeBlockForCall->clearEvalCache();
463     m_codeBlockForCall.clear();
464     if (m_codeBlockForConstruct)
465         m_codeBlockForConstruct->clearEvalCache();
466     m_codeBlockForConstruct.clear();
467     m_numParametersForCall = NUM_PARAMETERS_NOT_COMPILED;
468     m_numParametersForConstruct = NUM_PARAMETERS_NOT_COMPILED;
469
470 }
471
472 void FunctionExecutable::unlinkCalls()
473 {
474 #if ENABLE(JIT)
475     if (!!m_jitCodeForCall) {
476         ASSERT(m_codeBlockForCall);
477         m_codeBlockForCall->unlinkCalls();
478     }
479     if (!!m_jitCodeForConstruct) {
480         ASSERT(m_codeBlockForConstruct);
481         m_codeBlockForConstruct->unlinkCalls();
482     }
483 #endif
484 }
485
486 FunctionExecutable* FunctionExecutable::fromGlobalCode(const Identifier& functionName, ExecState* exec, Debugger* debugger, const SourceCode& source, JSObject** exception)
487 {
488     JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
489     RefPtr<ProgramNode> program = exec->globalData().parser->parse<ProgramNode>(lexicalGlobalObject, debugger, exec, source, 0, JSParseNormal, exception);
490     if (!program) {
491         ASSERT(*exception);
492         return 0;
493     }
494
495     // Uses of this function that would not result in a single function expression are invalid.
496     StatementNode* exprStatement = program->singleStatement();
497     ASSERT(exprStatement);
498     ASSERT(exprStatement->isExprStatement());
499     ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr();
500     ASSERT(funcExpr);
501     ASSERT(funcExpr->isFuncExprNode());
502     FunctionBodyNode* body = static_cast<FuncExprNode*>(funcExpr)->body();
503     ASSERT(body);
504
505     return FunctionExecutable::create(exec->globalData(), functionName, body->source(), body->usesArguments(), body->parameters(), body->isStrictMode(), body->lineNo(), body->lastLine());
506 }
507
508 UString FunctionExecutable::paramString() const
509 {
510     FunctionParameters& parameters = *m_parameters;
511     UStringBuilder builder;
512     for (size_t pos = 0; pos < parameters.size(); ++pos) {
513         if (!builder.isEmpty())
514             builder.append(", ");
515         builder.append(parameters[pos].ustring());
516     }
517     return builder.toUString();
518 }
519
520 }