2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/ext/factparse/parser.h"
22 #include "hphp/runtime/base/runtime-option.h"
23 #include "hphp/runtime/base/type-string.h"
24 #include "hphp/util/string-vsnprintf.h"
29 bool Parser::parseImpl() {
30 if (RuntimeOption::PHP7_UVS
) {
37 void Parser::parse() {
38 m_result
.error
= true;
41 throw ParseTimeFatalException(
45 getMessage().c_str());
47 std::stringstream(m_scanner
.getMd5().substr(0, 128/8))
49 >> m_result
.md5sum
[0];
50 std::stringstream(m_scanner
.getMd5().substr(128/8))
52 >> m_result
.md5sum
[1];
54 m_result
.error
= false;
57 void Parser::error(const char* fmt
, ...) {
61 string_vsnprintf(msg
, fmt
, ap
);
64 fatal(&m_loc
, msg
.c_str());
67 void Parser::parseFatal(const Location
* loc
, const char* msg
) {
68 // If the parser has a message, prepend it to the given message. Otherwise
69 // just use the given message.
70 std::string str
= getMessage();
78 auto exn
= ParseTimeFatalException(
79 m_fileName
, loc
->r
.line0
,
87 void Parser::fatal(const Location
* loc
, const char* msg
) {
88 throw ParseTimeFatalException(m_fileName
, loc
->r
.line0
, "%s", msg
);
91 void Parser::onClassStart(int /*type*/, Token
& /*name*/) {
95 // There can be multilple declarations of a class with the same name e.g.:
96 // if (...) { class X {function a(){}} } else { class X {function b(){}}}
97 // We take the union of base-types/traits across both declarations as this
98 // is what is needed to answer questions like "which classes extend Y".
100 // We also union the flags and if two declarations differ in "kind" this is set
101 // to MIXED. FactTypeFlags::MultipleDeclarations is always set if there are
102 // multiple declarations of the same type, even if all other features match.
104 // The flag for final is set for enums, and the flag for abstract is set for
105 // interfaces and traits.
106 void Parser::onClass(Token
& /*out*/, int type
, Token
& name
, Token
& base
,
107 Token
& baseInterface
, Token
& stmt
, Token
* /*attr*/,
108 Token
* /*enumBase*/) {
110 if (!isOutsideOfClass()) {
114 auto& name_data
= name
.text();
115 bool type_already_existed
=
116 m_result
.typesMap
.count(name_data
) != 0;
117 auto& type_details
= m_result
.typesMap
[name_data
];
122 kind_of
= FactKindOf::Enum
;
123 type_details
.flags
|= (uint64_t)FactTypeFlags::Final
;
126 kind_of
= FactKindOf::Interface
;
127 type_details
.flags
|= (uint64_t)FactTypeFlags::Abstract
;
130 kind_of
= FactKindOf::Trait
;
131 type_details
.flags
|= (uint64_t)FactTypeFlags::Abstract
;
134 kind_of
= FactKindOf::Class
;
137 kind_of
= FactKindOf::Class
;
138 type_details
.flags
|= (uint64_t)FactTypeFlags::Abstract
;
141 kind_of
= FactKindOf::Class
;
142 type_details
.flags
|= (uint64_t)FactTypeFlags::Final
;
144 case T_STATIC
: // abstract + Final
145 kind_of
= FactKindOf::Class
;
146 type_details
.flags
|=
147 (uint64_t)FactTypeFlags::Abstract
| (uint64_t)FactTypeFlags::Final
;
150 error("Unexpected class token: %d", type
);
152 if (type_already_existed
&& kind_of
!= type_details
.kindOf
) {
153 kind_of
= FactKindOf::Mixed
;
155 type_details
.kindOf
= kind_of
;
157 if (type_already_existed
) {
158 type_details
.flags
|= (uint64_t)FactTypeFlags::MultipleDeclarations
;
161 for (auto& baseType
: baseInterface
.m_baseTypes
) {
162 type_details
.baseTypes
.emplace(std::move(baseType
));
165 for (auto& baseType
: stmt
.m_baseTypes
) {
166 type_details
.baseTypes
.emplace(std::move(baseType
));
169 if (base
.text().size()) {
170 type_details
.baseTypes
.emplace(std::move(base
.takeText()));
173 if (!type_already_existed
&& type_details
.kindOf
== FactKindOf::Enum
) {
174 type_details
.baseTypes
.insert("HH\\BuiltinEnum");
177 if (kind_of
== FactKindOf::Trait
|| kind_of
== FactKindOf::Interface
) {
178 for (auto& requireExtend
: stmt
.m_requireExtends
) {
179 type_details
.requireExtends
.emplace(std::move(requireExtend
));
183 if (kind_of
== FactKindOf::Trait
) {
184 for (auto& requireImplement
: stmt
.m_requireImplements
) {
185 type_details
.requireImplements
.emplace(std::move(requireImplement
));
190 void Parser::onEnum(Token
& out
, Token
& name
, Token
& /*baseTy*/, Token
& /*stmt*/,
193 onClass(out
, T_ENUM
, name
, t
, t
, t
, nullptr, nullptr);
196 void Parser::onInterface(Token
& out
, Token
& name
, Token
& base
, Token
& stmt
,
199 onClass(out
, T_INTERFACE
, name
, t
, base
, stmt
, nullptr, nullptr);
202 void Parser::onInterfaceName(Token
&out
, Token
*names
, Token
&name
) {
204 out
= Token(std::move(*names
));
206 out
.m_baseTypes
.emplace(std::move(name
.takeText()));
209 void Parser::onTypedef(Token
& /*out*/, Token
& name
, const Token
& /*type*/,
210 const Token
* /*attr*/) {
211 if (!isOutsideOfClass()) {
214 m_result
.typeAliases
.emplace_back(std::move(name
.takeText()));
217 void Parser::onConst(Token
& /*out*/, Token
& name
, Token
& /*value*/) {
218 if (!isOutsideOfClass()) {
221 m_result
.constants
.emplace_back(std::move(name
.takeText()));
224 void Parser::onFunctionStart(Token
&name
) {
228 if (!isOutsideOfClass()) {
231 m_result
.functions
.emplace_back(std::move(name
.takeText()));
234 void Parser::onFunction(Token
& /*out*/, Token
* /*modifiers*/, Token
& /*ret*/,
235 Token
& /*ref*/, Token
& /*name*/, Token
& /*params*/,
236 Token
& /*stmt*/, Token
* /*attr*/) {
240 std::string
Parser::nsDecl(const std::string
&name
) {
241 if (m_namespace
.empty()) {
244 return m_namespace
+ NAMESPACE_SEP
+ name
;
247 std::string
Parser::nsClassDecl(const std::string
&name
) {
248 // Doesn't need to be implemented fully as we only deal with top-level
252 void Parser::onNamespaceStart(
253 const std::string
&ns
,
254 bool file_scope
/* =false */)
260 m_nsStack
.push_back(m_namespace
.size());
262 if (!m_namespace
.empty()) m_namespace
+= NAMESPACE_SEP
;
267 void Parser::onNamespaceEnd() {
268 m_namespace
.resize(m_nsStack
.back());
269 m_nsStack
.pop_back();
272 // This is needed when computing the name for onCall
273 std::string
Parser::resolve(const std::string
& ns
, bool /*cls*/) {
274 auto const pos
= ns
.find(NAMESPACE_SEP
);
276 // if qualified name, prepend current namespace
277 if (pos
!= std::string::npos
) {
281 // unqualified name in global namespace
282 if (m_namespace
.empty()) {
289 // Search for define("string", value)
290 void Parser::onCall(Token
& /*out*/, bool dynamic
, Token
& name
, Token
& params
,
295 && !strcasecmp(name
.text().c_str(), "define")
296 && params
.m_constArgs
== 2)
298 m_result
.constants
.emplace_back(std::move(params
.m_constantName
));
302 // Match an acceptable parameter list of the form ("string literal", <expr>)
303 void Parser::onCallParam(
310 // References or var-args are never the pattern we want
312 out
.m_constArgs
= -1;
315 out
= Token(std::move(*prevParams
));
316 if (out
.m_constArgs
!= - 1) {
319 } else if (expr
.num() == T_CONSTANT_ENCAPSED_STRING
) {
320 // First argument must be a string literal
321 out
.m_constantName
.swap(expr
.takeText());
324 out
.m_constArgs
= -1;
329 // Needed for onCallParam
330 void Parser::onScalar(Token
&out
, int type
, Token
&scalar
) {
331 out
= Token(std::move(scalar
));
335 // Needed to preserve fully qualified names of base interfaces
336 void Parser::onTypeAnnotation(Token
& out
, Token
& name
,
337 const Token
& /*typeArgs*/) {
341 // Trait requirements are accumulated as base types for the enclosing type
342 void Parser::onClassRequire(Token
&out
, Token
&name
, bool isExtends
) {
344 out
.m_requireExtends
.emplace(std::move(name
.takeText()));
346 out
.m_requireImplements
.emplace(std::move(name
.takeText()));
350 // Trait uses are accumulated as base types for the enclosing type
351 void Parser::onTraitUse(Token
& out
, Token
& traits
, Token
& /*rules*/) {
352 out
= Token(std::move(traits
));
355 void Parser::onTraitName(Token
&out
, Token
*names
, Token
&name
) {
357 out
= Token(std::move(*names
));
359 out
.m_baseTypes
.emplace(std::move(name
.takeText()));
362 // As we go through statements in a class we accumulate base types and trait
364 // out = stmts + new_stmt
365 void Parser::onClassStatement(Token
&out
, Token
&stmts
, Token
&new_stmt
) {
366 out
= Token(std::move(stmts
));
367 out
.takeAccumulate(new_stmt
);
370 void Parser::setTypeVars(Token
&out
, Token
&name
) {
371 out
= Token(std::move(name
));
375 /******************************************************************************
376 * EVERYTHING below this point is empty/unused/returns a falsey value *
377 ******************************************************************************/
379 void Parser::onScopeLabel(const Token
& /*stmt*/, const Token
& /*label*/) {}
381 void Parser::onCompleteLabelScope(bool /*fresh*/) {}
383 void Parser::onName(Token
& /*out*/, Token
& /*name*/, NameKind
/*kind*/) {}
385 void Parser::onStaticVariable(Token
& /*out*/, Token
* /*exprs*/, Token
& /*var*/,
388 void Parser::onClassVariable(Token
& /*out*/, Token
* /*exprs*/, Token
& /*var*/,
391 void Parser::onClassConstant(Token
& /*out*/, Token
* /*exprs*/, Token
& /*var*/,
394 void Parser::onClassAbstractConstant(Token
& /*out*/, Token
* /*exprs*/,
397 void Parser::onClassTypeConstant(Token
& /*out*/, Token
& /*var*/,
400 void Parser::onVariable(Token
& /*out*/, Token
* /*exprs*/, Token
& /*var*/,
401 Token
* /*value*/, bool /*constant*/ /* = false */,
402 const std::string
& /*docComment*/ /* = "" */) {}
404 void Parser::onSimpleVariable(Token
& /*out*/, Token
& /*var*/) {}
406 void Parser::onPipeVariable(Token
& /*out*/) {}
408 void Parser::onDynamicVariable(Token
& /*out*/, Token
& /*expr*/,
411 void Parser::onIndirectRef(Token
& /*out*/, Token
& /*refCount*/,
414 void Parser::onStaticMember(Token
& /*out*/, Token
& /*cls*/, Token
& /*name*/) {}
416 void Parser::onRefDim(Token
& /*out*/, Token
& /*var*/, Token
& /*offset*/) {}
418 void Parser::onObjectProperty(Token
& /*out*/, Token
& /*base*/,
419 PropAccessType
/*propAccessType*/,
422 void Parser::onObjectMethodCall(Token
& /*out*/, Token
& /*base*/,
423 bool /*nullsafe*/, Token
& /*prop*/,
424 Token
& /*params*/) {}
426 void Parser::onEncapsList(Token
& /*out*/, int /*type*/, Token
& /*list*/) {}
428 void Parser::addEncap(Token
& /*out*/, Token
* /*list*/, Token
& /*expr*/,
431 void Parser::encapRefDim(Token
& /*out*/, Token
& /*var*/, Token
& /*offset*/) {}
433 void Parser::encapObjProp(Token
& /*out*/, Token
& /*var*/,
434 PropAccessType
/*propAccessType*/, Token
& /*name*/) {}
436 void Parser::encapArray(Token
& /*out*/, Token
& /*var*/, Token
& /*expr*/) {}
438 void Parser::onConstantValue(Token
& /*out*/, Token
& /*constant*/) {}
440 void Parser::onExprListElem(Token
& /*out*/, Token
* /*exprs*/, Token
& /*expr*/) {
443 void Parser::onListAssignment(Token
& /*out*/, Token
& /*vars*/, Token
* /*expr*/,
444 bool /*rhsFirst*/ /* = false */) {}
446 void Parser::onAListVar(Token
& /*out*/, Token
* /*list*/, Token
* /*var*/) {}
448 void Parser::onAListSub(Token
& /*out*/, Token
* /*list*/, Token
& /*sublist*/) {}
450 void Parser::onAssign(Token
& /*out*/, Token
& /*var*/, Token
& /*expr*/,
451 bool /*ref*/, bool /*rhsFirst*/ /* = false */) {}
453 void Parser::onAssignNew(Token
& /*out*/, Token
& /*var*/, Token
& /*name*/,
456 void Parser::onNewObject(Token
& /*out*/, Token
& /*name*/, Token
& /*args*/) {}
458 void Parser::onUnaryOpExp(Token
& /*out*/, Token
& /*operand*/, int /*op*/,
461 void Parser::onBinaryOpExp(Token
& /*out*/, Token
& /*operand1*/,
462 Token
& /*operand2*/, int /*op*/) {}
464 void Parser::onQOp(Token
& /*out*/, Token
& /*exprCond*/, Token
* /*expYes*/,
467 void Parser::onNullCoalesce(Token
& /*out*/, Token
& /*expFirst*/,
468 Token
& /*expSecond*/) {}
470 void Parser::onArray(Token
& /*out*/, Token
& /*pairs*/,
471 int /*op*/ /* = T_ARRAY */) {}
473 void Parser::onDict(Token
& /*out*/, Token
& /*pairs*/) {}
475 void Parser::onVec(Token
& /*out*/, Token
& /*exprs*/) {}
477 void Parser::onKeyset(Token
& /*out*/, Token
& /*exprs*/) {}
479 void Parser::onVArray(Token
& /*out*/, Token
& /*exprs*/) {}
481 void Parser::onDArray(Token
& /*out*/, Token
& /*exprs*/) {}
483 void Parser::onArrayPair(Token
& /*out*/, Token
* /*pairs*/, Token
* /*name*/,
484 Token
& /*value*/, bool /*ref*/) {}
486 void Parser::onEmptyCollection(Token
& /*out*/) {}
488 void Parser::onUserAttribute(Token
& /*out*/, Token
* /*attrList*/,
489 Token
& /*name*/, Token
& /*value*/) {}
491 void Parser::onClassConst(Token
& /*out*/, Token
& /*cls*/, Token
& /*name*/,
494 void Parser::onClassClass(Token
& /*out*/, Token
& /*cls*/, Token
& /*name*/,
495 bool /*inStaticContext*/) {}
497 void Parser::onMethodStart(Token
& /*name*/, Token
& /*mods*/,
498 bool /*doPushComment*/ /* = true */) {}
500 void Parser::onMethod(Token
& /*out*/, Token
& /*modifiers*/, Token
& /*ret*/,
501 Token
& /*ref*/, Token
& /*name*/, Token
& /*params*/,
502 Token
& /*stmt*/, Token
* /*attr*/,
503 bool /*reloc*/ /* = true */) {}
505 void Parser::onVariadicParam(Token
& /*out*/, Token
* /*params*/, Token
& /*type*/,
506 Token
& /*var*/, bool /*ref*/, Token
* /*attr*/,
507 Token
* /*modifier*/) {}
509 void Parser::onParam(Token
& /*out*/, Token
* /*params*/, Token
& /*type*/,
510 Token
& /*var*/, bool /*ref*/, Token
* /*defValue*/,
511 Token
* /*attr*/, Token
* /*modifier*/) {}
513 void Parser::onClassExpressionStart() {
516 void Parser::onClassExpression(Token
& /*out*/, Token
& /*args*/, Token
& /*base*/,
517 Token
& /*baseInterface*/, Token
& /*stmt*/) {}
519 void Parser::onTraitRule(Token
& /*out*/, Token
& /*stmtList*/,
520 Token
& /*newStmt*/) {}
522 void Parser::onTraitPrecRule(Token
& /*out*/, Token
& /*traitName*/,
523 Token
& /*methodName*/, Token
& /*otherTraits*/) {}
525 void Parser::onTraitAliasRuleStart(Token
& /*out*/, Token
& /*traitName*/,
526 Token
& /*methodName*/) {}
528 void Parser::onTraitAliasRuleModify(Token
& /*out*/, Token
& /*rule*/,
529 Token
& /*accessModifiers*/,
530 Token
& /*newMethodName*/) {}
532 void Parser::onClassVariableStart(Token
& /*out*/, Token
* /*modifiers*/,
533 Token
& /*decl*/, Token
* /*type*/,
534 bool /*abstract*/ /* = false */,
535 bool /*typeconst*/ /* = false */) {}
537 void Parser::onMemberModifier(Token
& /*out*/, Token
* /*modifiers*/,
538 Token
& /*modifier*/) {}
540 void Parser::initParseTree() {
543 void Parser::finiParseTree() {
546 void Parser::onHaltCompiler() {
549 void Parser::onStatementListStart(Token
& /*out*/) {}
551 void Parser::addTopStatement(Token
& /*new_stmt*/) {}
553 void Parser::addStatement(Token
& /*out*/, Token
& /*stmts*/,
554 Token
& /*new_stmt*/) {}
556 void Parser::finishStatement(Token
& /*out*/, Token
& /*stmts*/) {}
558 void Parser::onBlock(Token
& /*out*/, Token
& /*stmts*/) {}
560 void Parser::onIf(Token
& /*out*/, Token
& /*cond*/, Token
& /*stmt*/,
561 Token
& /*elseifs*/, Token
& /*elseStmt*/) {}
563 void Parser::onElseIf(Token
& /*out*/, Token
& /*elseifs*/, Token
& /*cond*/,
566 void Parser::onWhile(Token
& /*out*/, Token
& /*cond*/, Token
& /*stmt*/) {}
568 void Parser::onDo(Token
& /*out*/, Token
& /*stmt*/, Token
& /*cond*/) {}
570 void Parser::onFor(Token
& /*out*/, Token
& /*expr1*/, Token
& /*expr2*/,
571 Token
& /*expr3*/, Token
& /*stmt*/) {}
573 void Parser::onSwitch(Token
& /*out*/, Token
& /*expr*/, Token
& /*cases*/) {}
575 void Parser::onCase(Token
& /*out*/, Token
& /*cases*/, Token
* /*cond*/,
578 void Parser::onBreakContinue(Token
& /*out*/, bool /*isBreak*/,
581 void Parser::onReturn(Token
& /*out*/, Token
* /*expr*/) {}
583 void Parser::onYield(Token
& /*out*/, Token
* /*expr*/) {}
585 void Parser::onYieldFrom(Token
& /*out*/, Token
* /*expr*/) {}
587 void Parser::onYieldPair(Token
& /*out*/, Token
* /*key*/, Token
* /*val*/) {}
589 void Parser::onYieldBreak(Token
& /*out*/) {}
591 void Parser::onAwait(Token
& /*out*/, Token
& /*expr*/) {}
593 void Parser::onGlobal(Token
& /*out*/, Token
& /*expr*/) {}
595 void Parser::onGlobalVar(Token
& /*out*/, Token
* /*exprs*/, Token
& /*expr*/) {}
597 void Parser::onStatic(Token
& /*out*/, Token
& /*expr*/) {}
599 void Parser::onHashBang(Token
& /*out*/, Token
& /*text*/) {}
601 void Parser::onEcho(Token
& /*out*/, Token
& /*expr*/, bool /*html*/) {}
603 void Parser::onUnset(Token
& /*out*/, Token
& /*expr*/) {}
605 void Parser::onExpStatement(Token
& /*out*/, Token
& /*expr*/) {}
607 void Parser::onForEach(Token
& /*out*/, Token
& /*arr*/, Token
& /*name*/,
608 Token
& /*value*/, Token
& /*stmt*/, bool /*awaitAs*/) {}
610 void Parser::onTry(Token
& /*out*/, Token
& /*tryStmt*/, Token
& /*className*/,
611 Token
& /*var*/, Token
& /*catchStmt*/, Token
& /*catches*/,
612 Token
& /*finallyStmt*/) {}
614 void Parser::onTry(Token
& /*out*/, Token
& /*tryStmt*/, Token
& /*finallyStmt*/) {
617 void Parser::onCatch(Token
& /*out*/, Token
& /*catches*/, Token
& /*className*/,
618 Token
& /*var*/, Token
& /*stmt*/) {}
620 void Parser::onFinally(Token
& /*out*/, Token
& /*stmt*/) {}
622 void Parser::onThrow(Token
& /*out*/, Token
& /*expr*/) {}
624 void Parser::onClosureStart(Token
& /*name*/) {}
626 Token
Parser::onClosure(ClosureType
/*type*/, Token
* /*modifiers*/,
627 Token
& /*ref*/, Token
& /*params*/, Token
& /*cparams*/,
628 Token
& /*stmts*/, Token
& /*ret1*/,
629 Token
* /*ret2*/ /* = nullptr */) {
633 Token
Parser::onExprForLambda(const Token
& /*expr*/) {
637 void Parser::onClosureParam(Token
& /*out*/, Token
* /*params*/, Token
& /*param*/,
640 void Parser::onLabel(Token
& /*out*/, Token
& /*label*/) {}
642 void Parser::onGoto(Token
& /*out*/, Token
& /*label*/, bool /*limited*/) {}
644 void Parser::onTypeList(Token
& /*type1*/, const Token
& /*type2*/) {}
646 void Parser::onClsCnsShapeField(Token
& /*out*/, const Token
& /*cls*/,
647 const Token
& /*cns*/, const Token
& /*value*/) {}
649 void Parser::onShapeFieldSpecialization(Token
& /*shapeField*/,
650 char /*specialization*/) {}
652 void Parser::onShape(Token
& /*out*/, const Token
& /*shapeFieldsList*/,
653 bool /*terminatedWithEllipsis*/) {}
655 void Parser::onTypeSpecialization(Token
& /*type*/, char /*specialization*/) {}
657 void Parser::onUseDeclaration(Token
& /*out*/, const std::string
& /*ns*/,
658 const std::string
& /*as*/) {}
660 void Parser::onMixedUseDeclaration(Token
& /*out*/, Token
& /*use*/,
661 UseDeclarationConsumer
/*f*/) {}
663 void Parser::onUse(const Token
& /*tok*/, UseDeclarationConsumer
/*f*/) {}
665 void Parser::onGroupUse(const std::string
& /*prefix*/, const Token
& /*tok*/,
666 UseDeclarationConsumer
/*f*/) {}
668 void Parser::onDeclare(Token
& /*out*/, Token
& /*block*/) {}
670 void Parser::onDeclareList(Token
& /*out*/, Token
& /*ident*/, Token
& /*exp*/) {}
672 void Parser::nns(int /*token*/ /* = 0 */,
673 const std::string
& /*text*/ /* = std::string() */) {}
675 void Parser::useClassAndNamespace(const std::string
& /*fn*/,
676 const std::string
& /*as*/) {}
678 void Parser::useClass(const std::string
& /*fn*/, const std::string
& /*as*/) {}
680 void Parser::useNamespace(const std::string
& /*fn*/,
681 const std::string
& /*as*/) {}
683 void Parser::useFunction(const std::string
& /*fn*/, const std::string
& /*as*/) {
686 void Parser::useConst(const std::string
& /*cnst*/, const std::string
& /*as*/) {}
688 void Parser::invalidateGoto(TStatementPtr
/*stmt*/, GotoError
/*error*/) {}
690 void Parser::invalidateLabel(TStatementPtr
/*stmt*/) {}
692 TStatementPtr
Parser::extractStatement(ScannerToken
* /*stmt*/) {
696 void Parser::onNewLabelScope(bool /*fresh*/) {}
698 /******************************************************************************
699 * ONLY put new stubs here. Put implemented methods further up with the rest. *
700 ******************************************************************************/