示例#1
0
    def expand_frames(self, frames):
        last_state = None
        state = None

        cache = self.cache
        sourcemaps = self.sourcemaps
        all_errors = []

        for frame in frames:
            errors = cache.get_errors(frame.abs_path)
            if errors:
                all_errors.extend(errors)

            source = cache.get(frame.abs_path)
            if source is None:
                logger.info('No source found for %s', frame.abs_path)
                continue

            sourcemap_url, sourcemap_idx = sourcemaps.get_link(frame.abs_path)
            if sourcemap_idx and frame.colno is not None:
                last_state = state
                state = find_source(sourcemap_idx, frame.lineno, frame.colno)

                if is_data_uri(sourcemap_url):
                    sourcemap_label = frame.abs_path
                else:
                    sourcemap_label = sourcemap_url

                abs_path = urljoin(sourcemap_url, state.src)

                logger.debug('Mapping compressed source %r to mapping in %r', frame.abs_path, abs_path)
                source = cache.get(abs_path)
                if not source:
                    frame.data = {
                        'sourcemap': sourcemap_label,
                    }
                    errors = cache.get_errors(abs_path)
                    if errors:
                        all_errors.extend(errors)
                    else:
                        all_errors.append({
                            'type': EventError.JS_MISSING_SOURCE,
                            'url': force_bytes(abs_path, errors='replace'),
                        })

                # Store original data in annotation
                frame.data = {
                    'orig_lineno': frame.lineno,
                    'orig_colno': frame.colno,
                    'orig_function': frame.function,
                    'orig_abs_path': frame.abs_path,
                    'orig_filename': frame.filename,
                    'sourcemap': sourcemap_label,
                }

                # SourceMap's return zero-indexed lineno's
                frame.lineno = state.src_line + 1
                frame.colno = state.src_col
                # The offending function is always the previous function in the stack
                # Honestly, no idea what the bottom most frame is, so we're ignoring that atm
                if last_state:
                    frame.function = last_state.name or frame.function
                else:
                    frame.function = state.name or frame.function

                filename = state.src
                # special case webpack support
                # abs_path will always be the full path with webpack:/// prefix.
                # filename will be relative to that
                if abs_path.startswith('webpack:'):
                    filename = abs_path
                    # webpack seems to use ~ to imply "relative to resolver root"
                    # which is generally seen for third party deps
                    # (i.e. node_modules)
                    if '/~/' in filename:
                        filename = '~/' + abs_path.split('/~/', 1)[-1]
                    else:
                        filename = filename.split('webpack:///', 1)[-1]

                    # As noted above, '~/' means they're coming from node_modules,
                    # so these are not app dependencies
                    if filename.startswith('~/'):
                        frame.in_app = False
                    # And conversely, local dependencies start with './'
                    elif filename.startswith('./'):
                        frame.in_app = True

                    # We want to explicitly generate a webpack module name
                    frame.module = generate_module(filename)

                frame.abs_path = abs_path
                frame.filename = filename
                if not frame.module and abs_path.startswith(('http:', 'https:', 'webpack:')):
                    frame.module = generate_module(abs_path)

            elif sourcemap_url:
                frame.data = {
                    'sourcemap': sourcemap_url,
                }

            # TODO: theoretically a minified source could point to another mapped, minified source
            frame.pre_context, frame.context_line, frame.post_context = get_source_context(
                source=source, lineno=frame.lineno, colno=frame.colno or 0)
        return all_errors
示例#2
0
    def process_frame(self, processable_frame, processing_task):
        frame = processable_frame.frame
        token = None

        cache = self.cache
        sourcemaps = self.sourcemaps
        all_errors = []
        sourcemap_applied = False

        # can't fetch source if there's no filename present or no line
        if not frame.get('abs_path') or not frame.get('lineno'):
            return

        # can't fetch if this is internal node module as well
        # therefore we only process user-land frames (starting with /)
        # or those created by bundle/webpack internals
        if self.data.get('platform') == 'node' and \
                not frame.get('abs_path').startswith(('/', 'app:', 'webpack:')):
            return

        errors = cache.get_errors(frame['abs_path'])
        if errors:
            all_errors.extend(errors)

        # This might fail but that's okay, we try with a different path a
        # bit later down the road.
        source = self.get_sourceview(frame['abs_path'])

        in_app = None
        new_frame = dict(frame)
        raw_frame = dict(frame)

        sourcemap_url, sourcemap_view = sourcemaps.get_link(frame['abs_path'])
        self.sourcemaps_touched.add(sourcemap_url)
        if sourcemap_view and frame.get('colno') is None:
            all_errors.append(
                {
                    'type': EventError.JS_NO_COLUMN,
                    'url': http.expose_url(frame['abs_path']),
                }
            )
        elif sourcemap_view:
            if is_data_uri(sourcemap_url):
                sourcemap_label = frame['abs_path']
            else:
                sourcemap_label = sourcemap_url

            sourcemap_label = http.expose_url(sourcemap_label)

            if frame.get('function'):
                minified_function_name = frame['function']
                minified_source = self.get_sourceview(frame['abs_path'])
            else:
                minified_function_name = minified_source = None

            try:
                # Errors are 1-indexed in the frames, so we need to -1 to get
                # zero-indexed value from tokens.
                assert frame['lineno'] > 0, "line numbers are 1-indexed"
                token = sourcemap_view.lookup(frame['lineno'] - 1,
                                              frame['colno'] - 1,
                                              minified_function_name,
                                              minified_source)
            except Exception:
                token = None
                all_errors.append(
                    {
                        'type': EventError.JS_INVALID_SOURCEMAP_LOCATION,
                        'column': frame.get('colno'),
                        'row': frame.get('lineno'),
                        'source': frame['abs_path'],
                        'sourcemap': sourcemap_label,
                    }
                )

            # persist the token so that we can find it later
            processable_frame.data['token'] = token

            # Store original data in annotation
            new_frame['data'] = dict(frame.get('data') or {}, sourcemap=sourcemap_label)

            sourcemap_applied = True

            if token is not None:
                abs_path = urljoin(sourcemap_url, token.src)

                logger.debug(
                    'Mapping compressed source %r to mapping in %r', frame['abs_path'], abs_path
                )
                source = self.get_sourceview(abs_path)

            if source is None:
                errors = cache.get_errors(abs_path)
                if errors:
                    all_errors.extend(errors)
                else:
                    all_errors.append(
                        {
                            'type': EventError.JS_MISSING_SOURCE,
                            'url': http.expose_url(abs_path),
                        }
                    )

            if token is not None:
                # the tokens are zero indexed, so offset correctly
                new_frame['lineno'] = token.src_line + 1
                new_frame['colno'] = token.src_col + 1

                # Try to use the function name we got from symbolic
                original_function_name = token.function_name

                # In the ideal case we can use the function name from the
                # frame and the location to resolve the original name
                # through the heuristics in our sourcemap library.
                if original_function_name is None:
                    last_token = None

                    # Find the previous token for function name handling as a
                    # fallback.
                    if processable_frame.previous_frame and \
                       processable_frame.previous_frame.processor is self:
                        last_token = processable_frame.previous_frame.data.get('token')
                        if last_token:
                            original_function_name = last_token.name

                if original_function_name is not None:
                    new_frame['function'] = original_function_name

                filename = token.src
                # special case webpack support
                # abs_path will always be the full path with webpack:/// prefix.
                # filename will be relative to that
                if abs_path.startswith('webpack:'):
                    filename = abs_path
                    # webpack seems to use ~ to imply "relative to resolver root"
                    # which is generally seen for third party deps
                    # (i.e. node_modules)
                    if '/~/' in filename:
                        filename = '~/' + abs_path.split('/~/', 1)[-1]
                    else:
                        filename = filename.split('webpack:///', 1)[-1]

                    # As noted above:
                    # * [js/node] '~/' means they're coming from node_modules, so these are not app dependencies
                    # * [node] sames goes for `./node_modules/` and '../node_modules/', which is used when bundling node apps
                    # * [node] and webpack, which includes it's own code to bootstrap all modules and its internals
                    #   eg. webpack:///webpack/bootstrap, webpack:///external
                    if filename.startswith('~/') or \
                            '/node_modules/' in filename or \
                            not filename.startswith('./'):
                        in_app = False
                    # And conversely, local dependencies start with './'
                    elif filename.startswith('./'):
                        in_app = True
                    # We want to explicitly generate a webpack module name
                    new_frame['module'] = generate_module(filename)

                # while you could technically use a subpath of 'node_modules' for your libraries,
                # it would be an extremely complicated decision and we've not seen anyone do it
                # so instead we assume if node_modules is in the path its part of the vendored code
                elif '/node_modules/' in abs_path:
                    in_app = False

                if abs_path.startswith('app:'):
                    if filename and NODE_MODULES_RE.search(filename):
                        in_app = False
                    else:
                        in_app = True

                new_frame['abs_path'] = abs_path
                new_frame['filename'] = filename
                if not frame.get('module') and abs_path.startswith(
                    ('http:', 'https:', 'webpack:', 'app:')
                ):
                    new_frame['module'] = generate_module(abs_path)

        elif sourcemap_url:
            new_frame['data'] = dict(
                new_frame.get('data') or {}, sourcemap=http.expose_url(sourcemap_url)
            )

        # TODO: theoretically a minified source could point to
        # another mapped, minified source
        changed_frame = self.expand_frame(new_frame, source=source)

        # If we did not manage to match but we do have a line or column
        # we want to report an error here.
        if not new_frame.get('context_line') \
           and source and \
           new_frame.get('colno') is not None:
            all_errors.append(
                {
                    'type': EventError.JS_INVALID_SOURCEMAP_LOCATION,
                    'column': new_frame['colno'],
                    'row': new_frame['lineno'],
                    'source': new_frame['abs_path'],
                }
            )

        changed_raw = sourcemap_applied and self.expand_frame(raw_frame)
        if sourcemap_applied or all_errors or changed_frame or \
           changed_raw:
            if in_app is not None:
                new_frame['in_app'] = in_app
                raw_frame['in_app'] = in_app
            return [new_frame], [raw_frame] if changed_raw else None, all_errors
示例#3
0
文件: processor.py 项目: aibar/sentry
    def expand_frames(self, frames):
        last_state = None
        state = None
        has_changes = False

        cache = self.cache
        sourcemaps = self.sourcemaps

        for frame in frames:
            errors = cache.get_errors(frame.abs_path)
            if errors:
                has_changes = True

            frame.errors = errors

            source = cache.get(frame.abs_path)
            if source is None:
                logger.info('No source found for %s', frame.abs_path)
                continue

            sourcemap_url, sourcemap_idx = sourcemaps.get_link(frame.abs_path)
            if sourcemap_idx and frame.colno is not None:
                last_state = state
                state = find_source(sourcemap_idx, frame.lineno, frame.colno)
                abs_path = urljoin(sourcemap_url, state.src)

                logger.debug('Mapping compressed source %r to mapping in %r', frame.abs_path, abs_path)
                source = cache.get(abs_path)
                if not source:
                    frame.data = {
                        'sourcemap': sourcemap_url,
                    }
                    errors = cache.get_errors(abs_path)
                    if errors:
                        frame.errors.extend(errors)
                    else:
                        frame.errors.append(ERR_MISSING_SOURCE.format(
                            filename=abs_path.encode('utf-8'),
                        ))

                # Store original data in annotation
                frame.data = {
                    'orig_lineno': frame.lineno,
                    'orig_colno': frame.colno,
                    'orig_function': frame.function,
                    'orig_abs_path': frame.abs_path,
                    'orig_filename': frame.filename,
                    'sourcemap': sourcemap_url,
                }

                # SourceMap's return zero-indexed lineno's
                frame.lineno = state.src_line + 1
                frame.colno = state.src_col
                # The offending function is always the previous function in the stack
                # Honestly, no idea what the bottom most frame is, so we're ignoring that atm
                if last_state:
                    frame.function = last_state.name or frame.function
                else:
                    frame.function = state.name or frame.function
                frame.abs_path = abs_path
                frame.filename = state.src
                frame.module = generate_module(state.src)

            elif sourcemap_url:
                frame.data = {
                    'sourcemap': sourcemap_url,
                }

            # TODO: theoretically a minified source could point to another mapped, minified source
            frame.pre_context, frame.context_line, frame.post_context = get_source_context(
                source=source, lineno=frame.lineno, colno=frame.colno or 0)
示例#4
0
    def expand_frames(self, frames, release):
        last_state = None
        state = None

        cache = self.cache
        sourcemaps = self.sourcemaps
        all_errors = []
        sourcemap_applied = False

        for frame in frames:
            errors = cache.get_errors(frame.abs_path)
            if errors:
                all_errors.extend(errors)

            # can't fetch source if there's no filename present
            if not frame.abs_path:
                continue

            source = self.get_source(frame.abs_path, release)
            if source is None:
                logger.debug('No source found for %s', frame.abs_path)
                continue

            sourcemap_url, sourcemap_idx = sourcemaps.get_link(frame.abs_path)
            if sourcemap_idx and frame.colno is None:
                all_errors.append({
                    'type': EventError.JS_NO_COLUMN,
                    'url': expose_url(frame.abs_path),
                })
            elif sourcemap_idx:
                last_state = state

                if is_data_uri(sourcemap_url):
                    sourcemap_label = frame.abs_path
                else:
                    sourcemap_label = sourcemap_url

                sourcemap_label = expose_url(sourcemap_label)

                try:
                    state = find_source(sourcemap_idx, frame.lineno, frame.colno)
                except Exception:
                    state = None
                    all_errors.append({
                        'type': EventError.JS_INVALID_SOURCEMAP_LOCATION,
                        'column': frame.colno,
                        'row': frame.lineno,
                        'source': frame.abs_path,
                        'sourcemap': sourcemap_label,
                    })

                # Store original data in annotation
                # HACK(dcramer): we stuff things into raw which gets popped off
                # later when adding the raw_stacktrace attribute.
                raw_frame = frame.to_json()
                frame.data = {
                    'raw': raw_frame,
                    'sourcemap': sourcemap_label,
                }

                sourcemap_applied = True

                if state is not None:
                    abs_path = urljoin(sourcemap_url, state.src)

                    logger.debug('Mapping compressed source %r to mapping in %r', frame.abs_path, abs_path)
                    source = self.get_source(abs_path, release)

                if not source:
                    errors = cache.get_errors(abs_path)
                    if errors:
                        all_errors.extend(errors)
                    else:
                        all_errors.append({
                            'type': EventError.JS_MISSING_SOURCE,
                            'url': expose_url(abs_path),
                        })

                if state is not None:
                    # SourceMap's return zero-indexed lineno's
                    frame.lineno = state.src_line + 1
                    frame.colno = state.src_col
                    # The offending function is always the previous function in the stack
                    # Honestly, no idea what the bottom most frame is, so we're ignoring that atm
                    if last_state:
                        frame.function = last_state.name or frame.function
                    else:
                        frame.function = state.name or frame.function

                    filename = state.src
                    # special case webpack support
                    # abs_path will always be the full path with webpack:/// prefix.
                    # filename will be relative to that
                    if abs_path.startswith('webpack:'):
                        filename = abs_path
                        # webpack seems to use ~ to imply "relative to resolver root"
                        # which is generally seen for third party deps
                        # (i.e. node_modules)
                        if '/~/' in filename:
                            filename = '~/' + abs_path.split('/~/', 1)[-1]
                        else:
                            filename = filename.split('webpack:///', 1)[-1]

                        # As noted above, '~/' means they're coming from node_modules,
                        # so these are not app dependencies
                        if filename.startswith('~/'):
                            frame.in_app = False
                        # And conversely, local dependencies start with './'
                        elif filename.startswith('./'):
                            frame.in_app = True

                        # Update 'raw' copy to have same in_app status
                        raw_frame['in_app'] = frame.in_app

                        # We want to explicitly generate a webpack module name
                        frame.module = generate_module(filename)

                    frame.abs_path = abs_path
                    frame.filename = filename
                    if not frame.module and abs_path.startswith(('http:', 'https:', 'webpack:')):
                        frame.module = generate_module(abs_path)

            elif sourcemap_url:
                frame.data = {
                    'sourcemap': expose_url(sourcemap_url),
                }

            # TODO: theoretically a minified source could point to another mapped, minified source
            frame.pre_context, frame.context_line, frame.post_context = get_source_context(
                source=source, lineno=frame.lineno, colno=frame.colno or 0)

            if not frame.context_line and source:
                all_errors.append({
                    'type': EventError.JS_INVALID_SOURCEMAP_LOCATION,
                    'column': frame.colno,
                    'row': frame.lineno,
                    'source': frame.abs_path,
                })
        return all_errors, sourcemap_applied
示例#5
0
    def process_frame(self, processable_frame, processing_task):
        frame = processable_frame.frame
        last_token = None
        token = None

        cache = self.cache
        sourcemaps = self.sourcemaps
        all_errors = []
        sourcemap_applied = False

        # can't fetch source if there's no filename present
        if not frame.get('abs_path'):
            return

        errors = cache.get_errors(frame['abs_path'])
        if errors:
            all_errors.extend(errors)

        # This might fail but that's okay, we try with a different path a
        # bit later down the road.
        source = self.get_source(frame['abs_path'])

        in_app = None
        new_frame = dict(frame)
        raw_frame = dict(frame)

        sourcemap_url, sourcemap_view = sourcemaps.get_link(frame['abs_path'])
        if sourcemap_view and frame.get('colno') is None:
            all_errors.append({
                'type': EventError.JS_NO_COLUMN,
                'url': expose_url(frame['abs_path']),
            })
        elif sourcemap_view:
            last_token = token

            if is_data_uri(sourcemap_url):
                sourcemap_label = frame['abs_path']
            else:
                sourcemap_label = sourcemap_url

            sourcemap_label = expose_url(sourcemap_label)

            try:
                # Errors are 1-indexed in the frames, so we need to -1 to get
                # zero-indexed value from tokens.
                assert frame['lineno'] > 0, "line numbers are 1-indexed"
                token = sourcemap_view.lookup_token(
                    frame['lineno'] - 1, frame['colno'])
            except Exception:
                token = None
                all_errors.append({
                    'type': EventError.JS_INVALID_SOURCEMAP_LOCATION,
                    'column': frame.get('colno'),
                    'row': frame.get('lineno'),
                    'source': frame['abs_path'],
                    'sourcemap': sourcemap_label,
                })

            # Store original data in annotation
            new_frame['data'] = dict(frame.get('data') or {},
                                     sourcemap=sourcemap_label)

            sourcemap_applied = True

            if token is not None:
                abs_path = urljoin(sourcemap_url, token.src)

                logger.debug('Mapping compressed source %r to mapping in %r',
                             frame['abs_path'], abs_path)
                source = self.get_source(abs_path)

            if not source:
                errors = cache.get_errors(abs_path)
                if errors:
                    all_errors.extend(errors)
                else:
                    all_errors.append({
                        'type': EventError.JS_MISSING_SOURCE,
                        'url': expose_url(abs_path),
                    })

            if token is not None:
                # Token's return zero-indexed lineno's
                new_frame['lineno'] = token.src_line + 1
                new_frame['colno'] = token.src_col
                # The offending function is always the previous function in the stack
                # Honestly, no idea what the bottom most frame is, so we're ignoring that atm
                if last_token:
                    new_frame['function'] = last_token.name or frame.get('function')
                else:
                    new_frame['function'] = token.name or frame.get('function')

                filename = token.src
                # special case webpack support
                # abs_path will always be the full path with webpack:/// prefix.
                # filename will be relative to that
                if abs_path.startswith('webpack:'):
                    filename = abs_path
                    # webpack seems to use ~ to imply "relative to resolver root"
                    # which is generally seen for third party deps
                    # (i.e. node_modules)
                    if '/~/' in filename:
                        filename = '~/' + abs_path.split('/~/', 1)[-1]
                    else:
                        filename = filename.split('webpack:///', 1)[-1]

                    # As noted above, '~/' means they're coming from node_modules,
                    # so these are not app dependencies
                    if filename.startswith('~/'):
                        in_app = False
                    # And conversely, local dependencies start with './'
                    elif filename.startswith('./'):
                        in_app = True

                    # We want to explicitly generate a webpack module name
                    new_frame['module'] = generate_module(filename)

                if abs_path.startswith('app:'):
                    if NODE_MODULES_RE.search(filename):
                        in_app = False
                    else:
                        in_app = True

                new_frame['abs_path'] = abs_path
                new_frame['filename'] = filename
                if not frame.get('module') and abs_path.startswith(
                        ('http:', 'https:', 'webpack:', 'app:')):
                    new_frame['module'] = generate_module(abs_path)

        elif sourcemap_url:
            new_frame['data'] = dict(new_frame.get('data') or {},
                                     sourcemap=expose_url(sourcemap_url))

        # TODO: theoretically a minified source could point to
        # another mapped, minified source
        changed_frame = self.expand_frame(new_frame, source=source)

        if not new_frame.get('context_line') and source:
            all_errors.append({
                'type': EventError.JS_INVALID_SOURCEMAP_LOCATION,
                # Column might be missing here
                'column': new_frame.get('colno'),
                # Line might be missing here
                'row': new_frame.get('lineno'),
                'source': new_frame['abs_path'],
            })

        changed_raw = sourcemap_applied and self.expand_frame(raw_frame)
        if sourcemap_applied or all_errors or changed_frame or \
           changed_raw:
            if in_app is not None:
                new_frame['in_app'] = in_app
                raw_frame['in_app'] = in_app
            return [new_frame], [raw_frame] if changed_raw else None, all_errors
示例#6
0
    def process_frame(self, processable_frame, processing_task):
        frame = processable_frame.frame
        token = None

        cache = self.cache
        sourcemaps = self.sourcemaps
        all_errors = []
        sourcemap_applied = False

        # can't fetch source if there's no filename present or no line
        if not frame.get('abs_path') or not frame.get('lineno'):
            return

        # can't fetch if this is internal node module as well
        # therefore we only process user-land frames (starting with /)
        # or those created by bundle/webpack internals
        if self.data.get('platform') == 'node' and \
                not frame.get('abs_path').startswith(('/', 'app:', 'webpack:')):
            return

        errors = cache.get_errors(frame['abs_path'])
        if errors:
            all_errors.extend(errors)

        # This might fail but that's okay, we try with a different path a
        # bit later down the road.
        source = self.get_sourceview(frame['abs_path'])

        in_app = None
        new_frame = dict(frame)
        raw_frame = dict(frame)

        sourcemap_url, sourcemap_view = sourcemaps.get_link(frame['abs_path'])
        self.sourcemaps_touched.add(sourcemap_url)
        if sourcemap_view and frame.get('colno') is None:
            all_errors.append({
                'type': EventError.JS_NO_COLUMN,
                'url': http.expose_url(frame['abs_path']),
            })
        elif sourcemap_view:
            if is_data_uri(sourcemap_url):
                sourcemap_label = frame['abs_path']
            else:
                sourcemap_label = sourcemap_url

            sourcemap_label = http.expose_url(sourcemap_label)

            if frame.get('function'):
                minified_function_name = frame['function']
                minified_source = self.get_sourceview(frame['abs_path'])
            else:
                minified_function_name = minified_source = None

            try:
                # Errors are 1-indexed in the frames, so we need to -1 to get
                # zero-indexed value from tokens.
                assert frame['lineno'] > 0, "line numbers are 1-indexed"
                token = sourcemap_view.lookup(frame['lineno'] - 1,
                                              frame['colno'] - 1,
                                              minified_function_name,
                                              minified_source)
            except Exception:
                token = None
                all_errors.append({
                    'type': EventError.JS_INVALID_SOURCEMAP_LOCATION,
                    'column': frame.get('colno'),
                    'row': frame.get('lineno'),
                    'source': frame['abs_path'],
                    'sourcemap': sourcemap_label,
                })

            # persist the token so that we can find it later
            processable_frame.data['token'] = token

            # Store original data in annotation
            new_frame['data'] = dict(frame.get('data') or {},
                                     sourcemap=sourcemap_label)

            sourcemap_applied = True

            if token is not None:
                abs_path = urljoin(sourcemap_url, token.src)

                logger.debug('Mapping compressed source %r to mapping in %r',
                             frame['abs_path'], abs_path)
                source = self.get_sourceview(abs_path)

            if source is None:
                errors = cache.get_errors(abs_path)
                if errors:
                    all_errors.extend(errors)
                else:
                    all_errors.append({
                        'type': EventError.JS_MISSING_SOURCE,
                        'url': http.expose_url(abs_path),
                    })

            if token is not None:
                # the tokens are zero indexed, so offset correctly
                new_frame['lineno'] = token.src_line + 1
                new_frame['colno'] = token.src_col + 1

                # Try to use the function name we got from symbolic
                original_function_name = token.function_name

                # In the ideal case we can use the function name from the
                # frame and the location to resolve the original name
                # through the heuristics in our sourcemap library.
                if original_function_name is None:
                    last_token = None

                    # Find the previous token for function name handling as a
                    # fallback.
                    if processable_frame.previous_frame and \
                       processable_frame.previous_frame.processor is self:
                        last_token = processable_frame.previous_frame.data.get(
                            'token')
                        if last_token:
                            original_function_name = last_token.name

                if original_function_name is not None:
                    new_frame['function'] = original_function_name

                filename = token.src
                # special case webpack support
                # abs_path will always be the full path with webpack:/// prefix.
                # filename will be relative to that
                if abs_path.startswith('webpack:'):
                    filename = abs_path
                    # webpack seems to use ~ to imply "relative to resolver root"
                    # which is generally seen for third party deps
                    # (i.e. node_modules)
                    if '/~/' in filename:
                        filename = '~/' + abs_path.split('/~/', 1)[-1]
                    else:
                        filename = filename.split('webpack:///', 1)[-1]

                    # As noted above:
                    # * [js/node] '~/' means they're coming from node_modules, so these are not app dependencies
                    # * [node] sames goes for `./node_modules/`, which is used when bundling node apps
                    # * [node] and webpack, which includes it's own code to bootstrap all modules and its internals
                    #   eg. webpack:///webpack/bootstrap, webpack:///external
                    if filename.startswith('~/') or \
                            filename.startswith('./node_modules/') or \
                            not filename.startswith('./'):
                        in_app = False
                    # And conversely, local dependencies start with './'
                    elif filename.startswith('./'):
                        in_app = True

                    # We want to explicitly generate a webpack module name
                    new_frame['module'] = generate_module(filename)

                if abs_path.startswith('app:'):
                    if filename and NODE_MODULES_RE.search(filename):
                        in_app = False
                    else:
                        in_app = True

                new_frame['abs_path'] = abs_path
                new_frame['filename'] = filename
                if not frame.get('module') and abs_path.startswith(
                    ('http:', 'https:', 'webpack:', 'app:')):
                    new_frame['module'] = generate_module(abs_path)

        elif sourcemap_url:
            new_frame['data'] = dict(new_frame.get('data') or {},
                                     sourcemap=http.expose_url(sourcemap_url))

        # TODO: theoretically a minified source could point to
        # another mapped, minified source
        changed_frame = self.expand_frame(new_frame, source=source)

        # If we did not manage to match but we do have a line or column
        # we want to report an error here.
        if not new_frame.get('context_line') \
           and source and \
           new_frame.get('colno') is not None:
            all_errors.append({
                'type': EventError.JS_INVALID_SOURCEMAP_LOCATION,
                'column': new_frame['colno'],
                'row': new_frame['lineno'],
                'source': new_frame['abs_path'],
            })

        changed_raw = sourcemap_applied and self.expand_frame(raw_frame)
        if sourcemap_applied or all_errors or changed_frame or \
           changed_raw:
            if in_app is not None:
                new_frame['in_app'] = in_app
                raw_frame['in_app'] = in_app
            return [new_frame
                    ], [raw_frame] if changed_raw else None, all_errors