diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2025-09-08 20:04:36 -0400 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2025-10-21 13:52:39 -0400 |
| commit | cae26921cf2b2e564f66e58b77a3963fe36f6d2d (patch) | |
| tree | bfa7ec8de23c1de758463a894583086c8d0b050e | |
| parent | 11ad1fc28470b863326f7136cf9b5723a06e89d3 (diff) | |
Add constant renderer API
| -rw-r--r-- | binaryninjaapi.h | 49 | ||||
| -rw-r--r-- | binaryninjacore.h | 28 | ||||
| -rw-r--r-- | binaryview.cpp | 6 | ||||
| -rw-r--r-- | constantrenderer.cpp | 151 | ||||
| -rw-r--r-- | python/__init__.py | 1 | ||||
| -rw-r--r-- | python/constantrenderer.py | 180 | ||||
| -rw-r--r-- | python/examples/encoded_strings.py | 58 |
7 files changed, 473 insertions, 0 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index fd1363b2..96105e66 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -4811,6 +4811,7 @@ namespace BinaryNinja { public: StringRef(); explicit StringRef(BNStringRef* ref); + StringRef(const std::string& str); StringRef(const StringRef& other); StringRef(StringRef&& other); ~StringRef(); @@ -21277,6 +21278,54 @@ namespace BinaryNinja { std::vector<LinearDisassemblyLine>& lines ) override; }; + + class ConstantRenderer : public StaticCoreRefCountObject<BNConstantRenderer> + { + std::string m_nameForRegister; + + public: + ConstantRenderer(const std::string& name); + ConstantRenderer(BNConstantRenderer* renderer); + + std::string GetName() const; + + virtual bool IsValidForType(HighLevelILFunction* func, Type* type); + virtual bool RenderConstant(const HighLevelILInstruction& instr, Type* type, int64_t val, + HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence); + virtual bool RenderConstantPointer(const HighLevelILInstruction& instr, Type* type, int64_t val, + HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, + BNOperatorPrecedence precedence); + + /*! Registers the constant renderer. + + \param renderer The constant renderer to register. + */ + static void Register(ConstantRenderer* renderer); + + static Ref<ConstantRenderer> GetByName(const std::string& name); + static std::vector<Ref<ConstantRenderer>> GetRenderers(); + + private: + static bool IsValidForTypeCallback(void* ctxt, BNHighLevelILFunction* hlil, BNType* type); + static bool RenderConstantCallback(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, + int64_t val, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, + BNOperatorPrecedence precedence); + static bool RenderConstantPointerCallback(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, + int64_t val, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, + BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence); + }; + + class CoreConstantRenderer : public ConstantRenderer + { + public: + CoreConstantRenderer(BNConstantRenderer* renderer); + bool IsValidForType(HighLevelILFunction* func, Type* type) override; + bool RenderConstant(const HighLevelILInstruction& instr, Type* type, int64_t val, + HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence) override; + bool RenderConstantPointer(const HighLevelILInstruction& instr, Type* type, int64_t val, + HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, + BNOperatorPrecedence precedence) override; + }; } // namespace BinaryNinja diff --git a/binaryninjacore.h b/binaryninjacore.h index 60850f38..89037998 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -311,6 +311,7 @@ extern "C" typedef struct BNStringRef BNStringRef; typedef struct BNIndirectBranchInfo BNIndirectBranchInfo; typedef struct BNArchitectureAndAddress BNArchitectureAndAddress; + typedef struct BNConstantRenderer BNConstantRenderer; typedef struct BNRemoteFileSearchMatch { @@ -3814,6 +3815,17 @@ extern "C" char* value; } BNTypeAttribute; + typedef struct BNCustomConstantRenderer + { + void* context; + bool (*isValidForType)(void* ctxt, BNHighLevelILFunction* hlil, BNType* type); + bool (*renderConstant)(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, + BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, BNOperatorPrecedence precedence); + bool (*renderConstantPointer)(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, int64_t val, + BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, + BNOperatorPrecedence precedence); + } BNCustomConstantRenderer; + BINARYNINJACOREAPI char* BNAllocString(const char* contents); BINARYNINJACOREAPI char* BNAllocStringWithLength(const char* contents, size_t len); BINARYNINJACOREAPI void BNFreeString(char* str); @@ -8690,6 +8702,22 @@ extern "C" BINARYNINJACOREAPI const char* BNGetStringRefContents(BNStringRef* ref); BINARYNINJACOREAPI size_t BNGetStringRefSize(BNStringRef* ref); + // Constant Renderers + BINARYNINJACOREAPI BNConstantRenderer* BNRegisterConstantRenderer( + const char* name, BNCustomConstantRenderer* renderer); + BINARYNINJACOREAPI BNConstantRenderer* BNGetConstantRendererByName(const char* name); + BINARYNINJACOREAPI BNConstantRenderer** BNGetConstantRendererList(size_t* count); + BINARYNINJACOREAPI void BNFreeConstantRendererList(BNLanguageRepresentationFunctionType** renderers); + BINARYNINJACOREAPI char* BNGetConstantRendererName(BNConstantRenderer* renderer); + BINARYNINJACOREAPI bool BNIsConstantRendererValidForType( + BNConstantRenderer* renderer, BNHighLevelILFunction* il, BNType* type); + BINARYNINJACOREAPI bool BNConstantRendererRenderConstant(BNConstantRenderer* renderer, BNHighLevelILFunction* il, + size_t exprIndex, BNType* type, int64_t val, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, + BNOperatorPrecedence precedence); + BINARYNINJACOREAPI bool BNConstantRendererRenderConstantPointer(BNConstantRenderer* renderer, + BNHighLevelILFunction* il, size_t exprIndex, BNType* type, int64_t val, BNHighLevelILTokenEmitter* tokens, + BNDisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, BNOperatorPrecedence precedence); + #ifdef __cplusplus } #endif diff --git a/binaryview.cpp b/binaryview.cpp index 66b7e77f..292b11ac 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -662,6 +662,12 @@ StringRef::StringRef(BNStringRef* ref) } +StringRef::StringRef(const std::string& str) +{ + m_ref = BNCreateStringRefOfLength(str.c_str(), str.size()); +} + + StringRef::StringRef(const StringRef& other) { m_ref = BNDuplicateStringRef(other.m_ref); diff --git a/constantrenderer.cpp b/constantrenderer.cpp new file mode 100644 index 00000000..1080c846 --- /dev/null +++ b/constantrenderer.cpp @@ -0,0 +1,151 @@ +#include "binaryninjaapi.h" +#include "ffi.h" +#include "highlevelilinstruction.h" + +using namespace BinaryNinja; +using namespace std; + + +ConstantRenderer::ConstantRenderer(const std::string& name): m_nameForRegister(name) +{ + m_object = nullptr; +} + + +ConstantRenderer::ConstantRenderer(BNConstantRenderer* renderer) +{ + m_object = renderer; +} + + +string ConstantRenderer::GetName() const +{ + char* name = BNGetConstantRendererName(m_object); + string result = name; + BNFreeString(name); + return result; +} + + +bool ConstantRenderer::IsValidForType(HighLevelILFunction*, Type*) +{ + return true; +} + + +bool ConstantRenderer::RenderConstant(const HighLevelILInstruction&, Type*, int64_t, + HighLevelILTokenEmitter&, DisassemblySettings*, BNOperatorPrecedence) +{ + return false; +} + + +bool ConstantRenderer::RenderConstantPointer(const HighLevelILInstruction&, Type*, int64_t, + HighLevelILTokenEmitter&, DisassemblySettings*, BNSymbolDisplayType, BNOperatorPrecedence) +{ + return false; +} + + +void ConstantRenderer::Register(ConstantRenderer* renderer) +{ + BNCustomConstantRenderer callbacks; + callbacks.context = renderer; + callbacks.isValidForType = IsValidForTypeCallback; + callbacks.renderConstant = RenderConstantCallback; + callbacks.renderConstantPointer = RenderConstantPointerCallback; + + renderer->AddRefForRegistration(); + renderer->m_object = BNRegisterConstantRenderer(renderer->m_nameForRegister.c_str(), &callbacks); +} + + +bool ConstantRenderer::IsValidForTypeCallback(void* ctxt, BNHighLevelILFunction* hlil, BNType* type) +{ + ConstantRenderer* renderer = (ConstantRenderer*)ctxt; + Ref<HighLevelILFunction> hlilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(hlil)); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + return renderer->IsValidForType(hlilObj, typeObj); +} + + +bool ConstantRenderer::RenderConstantCallback(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, + int64_t val, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, + BNOperatorPrecedence precedence) +{ + ConstantRenderer* renderer = (ConstantRenderer*)ctxt; + Ref<HighLevelILFunction> hlilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(hlil)); + HighLevelILInstruction instr = hlilObj->GetExpr(expr); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens)); + Ref<DisassemblySettings> settingsObj = settings ? new DisassemblySettings(BNNewDisassemblySettingsReference(settings)) : nullptr; + return renderer->RenderConstant(instr, typeObj, val, *tokenObj, settingsObj, precedence); +} + + +bool ConstantRenderer::RenderConstantPointerCallback(void* ctxt, BNHighLevelILFunction* hlil, size_t expr, BNType* type, + int64_t val, BNHighLevelILTokenEmitter* tokens, BNDisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, + BNOperatorPrecedence precedence) +{ + ConstantRenderer* renderer = (ConstantRenderer*)ctxt; + Ref<HighLevelILFunction> hlilObj = new HighLevelILFunction(BNNewHighLevelILFunctionReference(hlil)); + HighLevelILInstruction instr = hlilObj->GetExpr(expr); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + Ref<HighLevelILTokenEmitter> tokenObj = new HighLevelILTokenEmitter(BNNewHighLevelILTokenEmitterReference(tokens)); + Ref<DisassemblySettings> settingsObj = settings ? new DisassemblySettings(BNNewDisassemblySettingsReference(settings)) : nullptr; + return renderer->RenderConstantPointer(instr, typeObj, val, *tokenObj, settingsObj, symbolDisplay, precedence); +} + + +Ref<ConstantRenderer> ConstantRenderer::GetByName(const std::string& name) +{ + BNConstantRenderer* renderer = BNGetConstantRendererByName(name.c_str()); + if (!renderer) + return nullptr; + return new CoreConstantRenderer(renderer); +} + + +vector<Ref<ConstantRenderer>> ConstantRenderer::GetRenderers() +{ + size_t count = 0; + BNConstantRenderer** renderers = BNGetConstantRendererList(&count); + + vector<Ref<ConstantRenderer>> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(new CoreConstantRenderer(renderers[i])); + + BNFreeConstantRendererList(renderers); + return result; +} + + +CoreConstantRenderer::CoreConstantRenderer(BNConstantRenderer* renderer): + ConstantRenderer(renderer) +{ +} + + +bool CoreConstantRenderer::IsValidForType(HighLevelILFunction* func, Type* type) +{ + return BNIsConstantRendererValidForType(m_object, func->GetObject(), type->GetObject()); +} + + +bool CoreConstantRenderer::RenderConstant(const HighLevelILInstruction& instr, Type* type, int64_t val, + HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNOperatorPrecedence precedence) +{ + return BNConstantRendererRenderConstant(m_object, instr.function->GetObject(), instr.exprIndex, + type->GetObject(), val, tokens.GetObject(), settings ? settings->GetObject() : nullptr, precedence); +} + + +bool CoreConstantRenderer::RenderConstantPointer(const HighLevelILInstruction& instr, Type* type, int64_t val, + HighLevelILTokenEmitter& tokens, DisassemblySettings* settings, BNSymbolDisplayType symbolDisplay, + BNOperatorPrecedence precedence) +{ + return BNConstantRendererRenderConstantPointer(m_object, instr.function->GetObject(), instr.exprIndex, + type->GetObject(), val, tokens.GetObject(), settings ? settings->GetObject() : nullptr, + symbolDisplay, precedence); +} diff --git a/python/__init__.py b/python/__init__.py index 77c64fce..acf8b293 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -82,6 +82,7 @@ from .fileaccessor import * from .languagerepresentation import * from .lineformatter import * from .renderlayer import * +from .constantrenderer import * # We import each of these by name to prevent conflicts between # log.py and the function 'log' which we don't import below from .log import ( diff --git a/python/constantrenderer.py b/python/constantrenderer.py new file mode 100644 index 00000000..abd98be7 --- /dev/null +++ b/python/constantrenderer.py @@ -0,0 +1,180 @@ +# Copyright (c) 2015-2025 Vector 35 Inc +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import traceback +import ctypes +from typing import Optional + +import binaryninja +from . import _binaryninjacore as core +from . import function +from . import enums +from .log import log_error_for_exception +from . import types +from . import highlevelil +from . import languagerepresentation + + +class _ConstantRendererMetaClass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + renderers = core.BNGetConstantRendererList(count) + assert renderers is not None, "core.BNGetConstantRendererList returned None" + try: + for i in range(0, count.value): + yield CoreConstantRenderer(handle=renderers[i]) + finally: + core.BNFreeConstantRendererList(renderers) + + def __getitem__(cls, value): + binaryninja._init_plugins() + renderer = core.BNGetConstantRendererByName(str(value)) + if renderer is None: + raise KeyError("'%s' is not a valid renderer" % str(value)) + return CoreConstantRenderer(handle=renderer) + + +class ConstantRenderer(metaclass=_ConstantRendererMetaClass): + _registered_renderers = [] + renderer_name = None + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNConstantRenderer) + + def register(self): + if self.__class__.renderer_name is None: + raise ValueError("Renderer name is missing") + self._cb = core.BNCustomConstantRenderer() + self._cb.context = 0 + if self.is_valid_for_type.__func__ != ConstantRenderer.is_valid_for_type: + self._cb.isValidForType = self._cb.isValidForType.__class__(self._is_valid_for_type) + if self.render_constant.__func__ != ConstantRenderer.render_constant: + self._cb.renderConstant = self._cb.renderConstant.__class__(self._render_constant) + if self.render_constant_pointer.__func__ != ConstantRenderer.render_constant_pointer: + self._cb.renderConstantPointer = self._cb.renderConstantPointer.__class__(self._render_constant_pointer) + self.handle = core.BNRegisterConstantRenderer(self.__class__.renderer_name, self._cb) + self.__class__._registered_renderers.append(self) + + def _is_valid_for_type(self, ctxt, hlil, type): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + type = types.Type.create(handle=core.BNNewTypeReference(type)) + return self.is_valid_for_type(hlil, type) + except: + log_error_for_exception("Unhandled Python exception in ConstantRenderer._is_valid_for_type") + return False + + def _render_constant(self, ctxt, hlil, expr, type, val, tokens, settings, precedence): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + type = types.Type.create(handle=core.BNNewTypeReference(type)) + tokens = languagerepresentation.HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) + if settings: + settings = function.DisassemblySettings(core.BNNewDisassemblySettingsReference(settings)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr)) + return self.render_constant(instr, type, val, tokens, settings, precedence) + except: + log_error_for_exception("Unhandled Python exception in ConstantRenderer._render_constant_pointer") + return False + + def _render_constant_pointer(self, ctxt, hlil, expr, type, val, tokens, settings, symbol_display, precedence): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + type = types.Type.create(handle=core.BNNewTypeReference(type)) + tokens = languagerepresentation.HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) + if settings: + settings = function.DisassemblySettings(core.BNNewDisassemblySettingsReference(settings)) + symbol_display = enums.SymbolDisplayType(symbol_display) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr)) + return self.render_constant_pointer(instr, type, val, tokens, settings, symbol_display, precedence) + except: + log_error_for_exception("Unhandled Python exception in ConstantRenderer._render_constant_pointer") + return False + + @property + def name(self) -> str: + if hasattr(self, 'handle'): + return core.BNGetConstantRendererName(self.handle) + return self.__class__.renderer_name + + def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool: + return True + + def render_constant( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, + tokens: 'languagerepresentation.HighLevelILTokenEmitter', + settings: Optional['function.DisassemblySettings'], precedence: 'enums.OperatorPrecedence' + ) -> bool: + return False + + def render_constant_pointer( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, + tokens: 'languagerepresentation.HighLevelILTokenEmitter', + settings: Optional['function.DisassemblySettings'], symbol_display: 'enums.SymbolDisplayType', + precedence: 'enums.OperatorPrecedence' + ) -> bool: + return False + + +_renderer_cache = {} + + +class CoreConstantRenderer(ConstantRenderer): + def __init__(self, handle: core.BNConstantRenderer): + super(CoreConstantRenderer, self).__init__(handle=handle) + if type(self) is CoreConstantRenderer: + global _renderer_cache + _renderer_cache[ctypes.addressof(handle.contents)] = self + + @classmethod + def _from_cache(cls, handle) -> 'ConstantRenderer': + """ + Look up a renderer type from a given BNConstantRenderer handle + :param handle: BNConstantRenderer pointer + :return: Renderer type instance responsible for this handle + """ + global _renderer_cache + return _renderer_cache.get(ctypes.addressof(handle.contents)) or cls(handle) + + def is_valid_for_type(self, func: 'highlevelil.HighLevelILFunction', type: 'types.Type') -> bool: + return core.BNIsConstantRendererValidForType(self.handle, func.handle, type.handle) + + def render_constant( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, + tokens: 'languagerepresentation.HighLevelILTokenEmitter', + settings: Optional['function.DisassemblySettings'], precedence: 'enums.OperatorPrecedence' + ) -> bool: + if settings is not None: + settings = settings.handle + return core.BNConstantRendererRenderConstant(self.handle, instr.function.handle, instr.expr_index, + type.handle, val, tokens.handle, settings, precedence) + + def render_constant_pointer( + self, instr: 'highlevelil.HighLevelILInstruction', type: 'types.Type', val: int, + tokens: 'languagerepresentation.HighLevelILTokenEmitter', + settings: Optional['function.DisassemblySettings'], symbol_display: 'enums.SymbolDisplayType', + precedence: 'enums.OperatorPrecedence' + ) -> bool: + if settings is not None: + settings = settings.handle + return core.BNConstantRendererRenderConstantPointer(self.handle, instr.function.handle, instr.expr_index, + type.handle, val, tokens.handle, settings, symbol_display, precedence) diff --git a/python/examples/encoded_strings.py b/python/examples/encoded_strings.py new file mode 100644 index 00000000..ce7d02db --- /dev/null +++ b/python/examples/encoded_strings.py @@ -0,0 +1,58 @@ +from binaryninja import (ConstantRenderer, PointerType, InstructionTextToken, InstructionTextTokenType, DataBuffer) + + +class EncodedStringConstantRenderer(ConstantRenderer): + renderer_name = "encoded_strings" + decoders = { + "xor_encoded": lambda encoded, key: encoded ^ key, + "sub_encoded": lambda encoded, key: (encoded - key) & 0xff, + "add_encoded": lambda encoded, key: (encoded + key) & 0xff + } + + def is_valid_for_type(self, func, type): + if not isinstance(type, PointerType): + return False + for name in self.__class__.decoders.keys(): + if name in type.target.attributes: + return True + return False + + def render_constant_pointer(self, instr, type, val, tokens, settings, precedence): + if not isinstance(type, PointerType): + return False + + values = None + decoder = None + for name in self.__class__.decoders.keys(): + if name in type.target.attributes: + try: + values = bytes.fromhex(type.target.attributes[name]) + decoder = self.__class__.decoders[name] + except: + return False + if values is None or decoder is None: + return False + + encoded_null = "encoded_null" in type.target.attributes + + result = b"" + i = 0 + while True: + byte = instr.function.view.read(val + i, 1) + if len(byte) != 1: + return False + if not encoded_null and byte[0] == 0: + break + byte = decoder(byte[0], values[i % len(values)]) + if byte == 0: + break + result += bytes([byte]) + i += 1 + + tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, "\"")) + tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, DataBuffer(result).escape())) + tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, "\"_enc")) + return True + + +EncodedStringConstantRenderer().register() |
